When running spotbugs on a Kolin project I get errors such as:
[ERROR] Private method com.example.CSVRecord.component1() is never called [com.example.CSVRecord] In CSVRecord.kt UPM_UNCALLED_PRIVATE_METHOD
on classes such as:
data class CSVRecord(private val columns: SortedSet<CSVColumn>) : Iterable<String> {
override fun iterator(): Iterator<String> {
return columns.map { it.value }.iterator()
}
}
I'm not really clear where component1 came from!
According to the Data Classes documentation:
The compiler automatically derives the following members from all
properties declared in the primary constructor:
equals()/hashCode() pair;
toString() of the form "User(name=John, age=42)";
componentN() functions corresponding to the properties in their order of declaration;
copy() function (see below).
This is one the features of data classes. The auto-generated componentN functions allow you to use Destructuring Declarations on this type of classes:
data class Result(val result: Int, val status: Status)
fun function(...): Result {
// computations
return Result(result, status)
}
// Now, to use this function:
val (result, status) = function(...)
Related
I have a list of clases that implement a specific interface. The ability to construct those clases or not is not static (so it's not possible to use when(className)), and can be configured so I want to be able to create some clases or call some methods based on a hashMap of allowed "constructors". Then if the key identifying a class is in present in the hashmap I can call the corresponding method, otherwise I can safely ignore. Let me illustrate:
Let's say I have an interface like
interface Instanceable {
data class Config(
val bar: Whatever
)
fun getIntance(config: Config): Instanceable
}
Then I have several (let's say 10) classes that implement this interface
class Implementation1() : Instanceable {
companion object {
const val ID = "INSTANCE_1"
}
private lateinit var foo: Whatever
override fun getIntance(config: Config) = Implementation1().also{ this#Implementation1.foo = config.bar }
}
I want to create a hashmap of the methods by the identifiers, so later down the lane I can grab the method from the hashMap by the key ID and just invoke() the value if it's there. Something like:
allowedInstances("INSTANCE_1")?.let{ it.invoke(someConfig) }
In order to do this I tried to create a hashMap of methods like this:
private val allowedInstances = mutableHashMapOf<String, Instanceable.(Instanceable.Config)->Instanceable>()
allowedInstances[Instance1.ID] = Instance1::getIntance
allowedInstances[Instance2.ID] = Instance2::getIntance
allowedInstances[Instance4.ID] = Instance4::getIntance
But it fails with:
Type mismatch.
Required: Instanceable.(Instanceable.Config) → Instanceable
Found: KFunction2<Implementation1, Instanceable.Config, Instanceable>
If I create the hashmap directly and let the compiler infer the types like this:
private val allowedInstances = mutableHashMapOf(
Implementation1.ID to Implementation1::getIntance,
Implementation2.ID to Implementation2::getIntance,
Implementation4.ID to Implementation4::getIntance,
)
Checking the type of the hashmap shows:
HashMap<String, out KFunction2<Nothing, Instanceable.Config, Instanceable>>
In fact I can do:
private val allowedInstances = mutableHashMapOf<String, Nothing.(Instanceable.Config)->Instanceable>()
allowedInstances[Instance1.ID] = Instance1::getIntance
allowedInstances[Instance2.ID] = Instance2::getIntance
allowedInstances[Instance4.ID] = Instance4::getIntance
So the actual question is:
Why the function of the second hashMap parameter has Nothing as the receptor? Why I cannot have the interface Instanceable instead?
Edit: Still not good to have the Nothing there:
allowedInstances["INSTANCE_1"]?.let{ it.invoke(Nothing, someConfig) }
//Fails with: Classifier 'Nothing' does not have a companion object, and thus must be initialized here
Edit 2: All of the errors are in compile time
Your function type
Instanceable.(Instanceable.Config) -> Instanceable
is describing an extension function on an instance of Instanceable. You need to omit the receiver from the function type to be able to match your constructors' signature:
(Instanceable.Config) -> Instanceable
Edit: The other half of the problem is that you define getInstance() as a member function of the class. So you have to create an invalid instance of your class to use to create a valid instance, which doesn't make sense.
I would delete the getInstance() function from your interface, and put the equivalent code in the constructor of your class. Then you can define a function type in your Map that constructs your items.
interface Instanceable {
data class Config(
val bar: Whatever
)
// REMOVE this: fun getIntance(config: Config): Instanceable
}
class Implementation1(config: Config) : Instanceable {
companion object {
const val ID = "INSTANCE_1"
}
private val foo: Whatever = config.bar
}
private val allowedInstances = mutableHashMapOf<String, (Instanceable.Config)->Instanceable>()
allowedInstances[Instance1.ID] = ::Implementation1
// and so on...
// If there's an implementation that has no config, you can use a lambda:
class NoConfigImplementation : Instanceable {
companion object {
const val ID = "INSTANCE_2"
}
}
allowedInstances[NoConfigImplementation.ID] = { _ -> NoConfigImplementation() }
I have multiple data classes and each class has a corresponding class containing more info. I want to write a function in which I should be able to pass an identifier (table name corresponding to the data class). Based on this identifier, object of the corresponding class should be made, the value changed and this object should be returned as output of the function. I have written a simplified version of it on playground but I am unable to get it to work. Any help is appreciated.
class someClass(
)
class objectForSomeClass(
var value: String
)
class someOtherClass(
)
class objectForSomeOtherClass(
var value: String
)
class doSomething() {
companion object {
val classMap = mapOf(
"someClass" to objectForSomeClass::class,
"someOtherClass" to objectForSomeOtherClass::class,
)
}
// Create a map of class name to a new object based on the class name input
fun dummyFun(className: String, valueInput: String): Map<String, kotlin.Any> {
var returnObject = mutableListOf<Pair<String, kotlin.Any>>()
when(className) {
"SOME_CLASS" -> {
returnObject = mutableListOf<Pair<String, justDoIt.classMap["someClass"]()>>()
}
"SOME_OTHER_CLASS" -> {
returnObject = Map<String, justDoIt.classMap["someOtherClass"]()>
}
}
returnObject[className].value = valueInput
return returnObject
}
}
fun main() {
var obj = doSomething()
var t = obj.dummyFun("SOME_CLASS", "Value to be inserted")
// do something with t
}
Not knowing more about your classes (the ones in your code are not data classes – a data class in Kotlin is a specific type of class) I still think a lot could be simplified down to maybe even this:
fun createObject(className: String, value: String): Any? {
return when (className) {
"SomeClass" -> ObjectForSomeClass(value)
"SomeOtherClass" -> ObjectForSomeOtherClass(value)
// ...
else -> null
}
}
Additionally:
The classMap is not necessary, you can hard-code the cases in the when clause as in my example. There is also no need for reflection, which you would need to create instances from SomeType::class.
With getting rid of classMap you also do not need the companion object holding it anymore, and then you are left with one function for creating instances of your classes, and this function does not have to be in a class. You might put it into a singleton class called object in Kotlin (https://kotlinlang.org/docs/object-declarations.html#object-expressions)
Data classes in Kotlin: https://kotlinlang.org/docs/data-classes.html
You could maybe also replace each class someClass & class objectForSomeClass pair with a class someClass with a companion object.
I'm trying this:
class Foo<T> {
var member: T = T()
}
...but the Kotlin compiler gives me an error: Type parameter T cannot be called as function.
How do I default-construct a generic member variable?
Well, to access the type information, we need to use the reified keyword on the type, but this is only applicable in inlined functions. So instead of relying on direct construction, a workaround can be to use a generator function wrapped in the companion object that immediately sets the member right after construction
// Test class to verify the implementation
class Yolo {
override fun toString() = "Yolo swag"
}
class Foo<T : Any> {
lateinit var member: T
companion object {
inline fun <reified T : Any> newInstance() =
T::class.java.newInstance().let { memberInstance ->
Foo<T>().apply { member = memberInstance}
}
}
}
fun main() {
// generate a Foo<Yolo>
val foo = Foo.newInstance<Yolo>()
println(foo.member) // displays "Yolo swag"
}
It's implied that T has a public no-arg constructor, but in general case it may not be true. This code uses reflection to bypass compiler complains about it (which may end up with runtime error if you dissapoint the JVM expectations and indeed pass T without public no-arg constructor).
//Reified generics at class level are not yet supported in Kotlin (KT-33213),
// so you have to pass instance of `KClass` manually as a consructor parameter
class Foo<T : Any>(clazz: KClass<T>) {
var member: T = clazz.createInstance()
}
Having a interface defined:
interface IData {
fun getHash() : Int
fun getUUID(): UUID
......
}
Trying to create object for the interface. the fun getUUID(): UUID is fine, but the fun getHash() : Int got error as below.
What might be wrong? why they are different?
fun buidlDataList () : ArrayList<IData> {
val dataList = ArrayList<IData>(0)
dataList.add(object : IData {
val hash: Int by lazy { dataFetchers.size+System.currentTimeMillis().toInt() } //<=== get error
override fun getHash(): Int { //<=== get the same error
return hash
}
val uuid: UUID by lazy { UUID.randomUUID() }
override fun getUUID(): UUID {
return uuid
}
......
}
}
Platform declaration clash: The following declarations have the same JVM signature(getHash() I):
* public final fun <get-hash>(): int defined in com.data. buidlDataList <no name provided>
* public open fun getHash(): int defined in defined in com.data. buidlDataList <no name provided>
The variables create their own getters, but you also explicitly define them. When you declare a var or a val, they usually have their own getters automatically generated1. Private vals or vars don't, if you don't create a custom getter.
But in all other cases, this:
val x: Int = TODO()
generates a getter1.
In your case, I'd recommend using val in the interface directly. You see, the generated getter has the same name as the getHash method you explicitly declared. The getters don't override methods either (unless you annotate it with one of the #Jvm annotations, and I don't remember which, but you don't really need those anyways).
So you change your interface to:
interface IData {
val hash: Int
val uuid: UUID
}
And remove the getters in the overridden object, and add override to the vals:
dataList.add(object : IData {
override val hash: Int by lazy { dataFetchers.size+System.currentTimeMillis().toInt() }
override val uuid: UUID by lazy { UUID.randomUUID() }
}
The first interface is actually equivalent to declaring an interface with get and set methods. If you override it from Java, it will ask you to override getHash() and getUid(), and you need to declare the field locally. Kotlin works differently because it automatically generates setters.
And since you can declare variables in interfaces without messing up Java interop, I highly recommend you use that over #Jvm* annotations (mainly because it makes it easier to understand the code, though that's a personal preference).
Also, if you decompile the Kotlin bytecode, you'll see what the interface with the variables compiles to:
public interface IData {
int getHash();
#NotNull
UUID getUuid();
}
So it's identical to the one you originally had, just without conflicts in child classes because of variable name clashes.
And the reason only one has a clash is because, as you see in the interface, val uuid creates a getter named getUuid, while your interface declares getUUID. Methods in Java and Kotlin are case-sensitive, which is why those don't clash. If you re-name your variable to upper-case UUID, you will get a clash on that too.
1: Assuming the variable/constant isn't in a method. Top-level variables, variables in interfaces, enums, classes, objects, and companion objects all generate getters/setters, but if you declare a variable inside a method, it naturaly won't have getters and setters where that applies.
Since kotlin has such good support for lambdas, I started to use lambdas as constructor parameters for abstract classes instead of declaring abstract val/fun.
It's more concise in my opinion, especially because val type get's inferred.
What are the downsides to this?
abstract class AbstractListScreen<T> (
val data: Set<T>,
val filterators: (T) -> Set<String>
) {
fun open() {
/* ... */
}
}
class OrderListScreen : AbstractListScreen<Data>(data = setOf(),
filterators = { setOf(it.toString()) }
) {
fun someEvent() {
/* ...*/
}
}
In your example, each instance of OrderListScreen will create its own filterators instance of function type (T) -> Set<String>. This has additional run-time overhead in both memory and performance when compared with abstract functions and their overrides which are stored in the type definition at compile-time.
The default filterators can be stored in a property to reduce this run-time overhead:
class OrderListScreen : AbstractListScreen<Data>(data = setOf(),
filterators = defaultFilterators
) {
companion object {
val defaultFilterators: (Data) -> Set<String> = { setOf(it.toString()) }
}
fun someEvent() {
/* ...*/
}
}
However, each instance of OrderListScreen will still have its own reference to defaultFilterators which is still additional run-time overhead (although marginal unless you have many instances of these types).
Function types such as (T) -> Set<String> may have named parameters (e.g. (element: T) -> Set<String>) but currently IDEs such as IntelliJ IDEA do not use those named parameters in generated documentation or code stubs so such information is lost when subclassing, etc. IDEs do use named parameters in generated documentation and code stubs for abstract functions.
You cannot (currently) associate documentation directly with the function type parameter which you can do with abstract functions.
When attempting to account for the run-time overhead the code doesn't look much different when using abstract functions, the run-time overhead is eliminated, and current IDE support for generated code stubs, documentation, etc. is improved:
abstract class AbstractListScreen<T>(val data: Set<T>) {
abstract fun filterators(element: T): Set<String>
fun open() {
/* ... */
}
}
class OrderListScreen : AbstractListScreen<Data>(data = setOf()) {
override fun filterators(element: Data): Set<String> = setOf(element.toString())
fun someEvent() {
/* ...*/
}
}