How to get generic param class in Kotlin? - kotlin

I need to be able to tell the generic type of kotlin collection at runtime. How can I do it?
val list1 = listOf("my", "list")
val list2 = listOf(1, 2, 3)
val list3 = listOf<Double>()
/* ... */
when(list.genericType()) {
is String -> handleString(list)
is Int -> handleInt(list)
is Double -> handleDouble(list)
}

Kotlin generics share Java's characteristic of being erased at compile time, so, at run time, those lists no longer carry the necessary information to do what you're asking. The exception to this is if you write an inline function, using reified types. For example this would work:
inline fun <reified T> handleList(l: List<T>) {
when (T::class) {
Int::class -> handleInt(l)
Double::class -> handleDouble(l)
String::class -> handleString(l)
}
}
fun main() {
handleList(mutableListOf(1,2,3))
}
Inline functions get expanded at every call site, though, and mess with your stack traces, so you should use them sparingly.
Depending on what you're trying to achieve, though, there's some alternatives. You can achieve something similar at the element level with sealed classes:
sealed class ElementType {
class DoubleElement(val x: Double) : ElementType()
class StringElement(val s: String) : ElementType()
class IntElement(val i: Int) : ElementType()
}
fun handleList(l: List<ElementType>) {
l.forEach {
when (it) {
is ElementType.DoubleElement -> handleDouble(it.x)
is ElementType.StringElement -> handleString(it.s)
is ElementType.IntElement -> handleInt(it.i)
}
}
}

You can use inline functions with reified type parameters to do that:
inline fun <reified T : Any> classOfList(list: List<T>) = T::class
(runnable demo, including how to check the type in a when statement)
This solution is limited to the cases where the actual type argument for T is known at compile time, because inline functions are transformed at compile time, and the compiler substitutes their reified type parameters with the real type at each call site.
On JVM, the type arguments of generic classes are erased at runtime, and there is basically no way to retrieve them from an arbitrary List<T> (e.g. a list passed into a non-inline function as List<T> -- T is not known at compile-time for each call and is erased at runtime)
If you need more control over the reified type parameter inside the function, you might find this Q&A useful.

Related

Providing only one type parameter to an extension function with multiple type parameters in Kotlin

Introduction
In Kotlin I have a generic conversion extension function that simplifies conversion of this object of type C to an object of another type T (declared as the receiver) with additional conversion action that treats receiver as this and also provides access to original object:
inline fun <C, T, R> C.convertTo(receiver: T, action: T.(C) -> R) = receiver.apply {
action(this#convertTo)
}
It is used like this:
val source: Source = Source()
val result = source.convertTo(Result()) {
resultValue = it.sourceValue
// and so on...
}
I noticed I often use this function on receivers that are created by parameterless constructors and thought it would be nice to simplify it even more by creating additional version of convertTo() that automates construction of the receiver based on its type, like this:
inline fun <reified T, C, R> C.convertTo(action: T.(C) -> R) = with(T::class.constructors.first().call()) {
convertTo(this, action) // calling the first version of convertTo()
}
Unfortunately, I cannot call it like this:
source.convertTo<Result>() {}
because Kotlin expects three type parameters provided.
Question
Given above context, is it possible in Kotlin to create a generic function with multiple type parameters that accepts providing just one type parameter while other types are determined from the call-site?
Additional examples (by #broot)
Imagine there is no filterIsInstance() in stdlib and we would like to implement it (or we are the developer of stdlib). Assume we have access to #Exact as this is important for our example. It would be probably the best to declare it as:
inline fun <T, reified V : T> Iterable<#Exact T>.filterTyped(): List<V>
Now, it would be most convenient to use it like this:
val dogs = animals.filterTyped<Dog>() // compile error
Unfortunately, we have to use one of workarounds:
val dogs = animals.filterTyped<Animal, Dog>()
val dogs: List<Dog> = animals.filterTyped()
The last one isn't that bad.
Now, we would like to create a function that looks for items of a specific type and maps them:
inline fun <T, reified V : T, R> Iterable<T>.filterTypedAndMap(transform: (V) -> R): List<R>
Again, it would be nice to use it just like this:
animals.filterTypedAndMap<Dog> { it.barkingVolume } // compile error
Instead, we have this:
animals.filterTypedAndMap<Animal, Dog, Int> { it.barkingVolume }
animals.filterTypedAndMap { dog: Dog -> dog.barkingVolume }
This is still not that bad, but the example is intentionally relatively simple to make it easy to understand. In reality the function would be more complicated, would have more typed params, lambda would receive more arguments, etc. and then it would become hard to use. After receiving the error about type inference, the user would have to read the definition of the function thoroughly to understand, what is missing and where to provide explicit types.
As a side note: isn't it strange that Kotlin disallows code like this: cat is Dog, but allows this: cats.filterIsInstance<Dog>()? Our own filterTyped() would not allow this. So maybe (but just maybe), filterIsInstance() was designed like this exactly because of the problem described in this question (it uses * instead of additional T).
Another example, utilizing already existing reduce() function. We have function like this:
operator fun Animal.plus(other: Animal): Animal
(Don't ask, it doesn't make sense)
Now, reducing a list of dogs seems pretty straightforward:
dogs.reduce { acc, item -> acc + item } // compile error
Unfortunately, this is not possible, because compiler does not know how to properly infer S to Animal. We can't easily provide S only and even providing the return type does not help here:
val animal: Animal = dogs.reduce { acc, item -> acc + item } // compile error
We need to use some awkward workarounds:
dogs.reduce<Animal, Dog> { acc, item -> acc + item }
(dogs as List<Animal>).reduce { acc, item -> acc + item }
dogs.reduce { acc: Animal, item: Animal -> acc + item }
The type parameter R is not necessary:
inline fun <C, T> C.convertTo(receiver: T, action: T.(C) -> Unit) = receiver.apply {
action(this#convertTo)
}
inline fun <reified T, C> C.convertTo(action: T.(C) -> Unit) = with(T::class.constructors.first().call()) {
convertTo(this, action) // calling the first version of convertTo()
}
If you use Unit, even if the function passed in has a non-Unit return type, the compiler still allows you to pass that function.
And there are other ways to help the compiler infer the type parameters, not only by directly specifying them in <>. You can also annotate the variable's result type:
val result: Result = source.convertTo { ... }
You can also change the name of convertTo to something like convert to make it more readable.
Another option is:
inline fun <T: Any, C> C.convertTo(resultType: KClass<T>, action: T.(C) -> Unit) = with(resultType.constructors.first().call()) {
convertTo(this, action)
}
val result = source.convertTo(Result::class) { ... }
However, this will conflict with the first overload. So you have to resolve it somehow. You can rename the first overload, but I can't think of any good names off the top of my head. I would suggest that you specify the parameter name like this
source.convertTo(resultType = Result::class) { ... }
Side note: I'm not sure if the parameterless constructor is always the first in the constructors list. I suggest that you actually find the parameterless constructor.
This answer does not solve the stated problem but incorporates input from #Sweeper to provide a workaround at least simplifying result object instantiation.
First of all, the main stated problem can be somewhat mitigated if we explicitly state variable's result type (i.e. val result: Result = source.convertTo {}) but it's not enough to solve the problem in cases described by #broot.
Secondly, using KClass<T> as result parameter type provides ability to use KClass<T>.createInstance() making sure we find a parameterless constructor (if there's any – if there is none, then result-instantiating convertTo() is not eligible for use). We can also benefit from Kotlin's default parameter values to make result parameter type omittable from calls, we just need to take into account that action might be provided as lambda (last parameter of call) or function reference – this will require two versions of result-instantiating convertTo().
So, taking all the above into account, I've come up with this implementation(s) of convertTo():
// version A: basic, expects explicitly provided instance of `receiver`
inline fun <C, T> C.convertTo(receiver: T, action: T.(C) -> Unit) = receiver.apply {
action(this#convertTo)
}
// version B: can instantiate result of type `T`, supports calls where `action` is a last lambda
inline fun <C, reified T : Any> C.convertTo(resultType: KClass<T> = T::class, action: T.(C) -> Unit) = with(resultType.createInstance()) {
(this#convertTo).convertTo(this#with, action)
}
// version C: can instantiate result of type `T`, supports calls where `action` is passed by reference
inline fun <C, reified T : Any> C.convertTo(action: T.(C) -> Unit, resultType: KClass<T> = T::class) = with(resultType.createInstance()) {
(this#convertTo).convertTo(T::class, action)
}
All three versions work together depending on a specific use case. Below is a set of examples explaining what version is used in what case.
class Source { var sourceId = "" }
class Result { var resultId = "" }
val source = Source()
fun convertX(result: Result, source: Source) {
result.resultId = source.sourceId
}
fun convertY(result: Result, source: Source) = true
fun Source.toResultX(): Result = convertTo { resultId = it.sourceId }
fun Source.toResultY(): Result = convertTo(::convertX)
val result0 = source.convertTo(Result()) { resultId = it.sourceId } // uses version A of convertTo()
val result1: Result = source.convertTo { resultId = it.sourceId } // uses version B of convertTo()
val result2: Result = source.convertTo(::convertX) // uses version C of convertTo()
val result3: Result = source.convertTo(::convertY) // uses version C of convertTo()
val result4: Result = source.toResultX() // uses version B of convertTo()
val result5: Result = source.toResultY() // uses version C of convertTo()
P.S.: As #Sweeper notices, convertTo might not be a good name for the result-instantiating versions (as it's not as readable as with basic version) but that's a secondary problem.

Overload resolution of reified generic in Kotlin

I would like to return different type depending on a reified type parameter. I tried to use overloads but the overload resolution doesn't seem correct.
My goal is to store a close set of types at runtime like so:
sealed interface Type<T> {
object Int: Type<kotlin.Int>
object Boolean: Type<kotlin.Boolean>
}
inline fun<reified T> get() : Type<T> = getImpl(null as T?)
fun getImpl(a: Int?) : Type<Int> = Type.Int
fun getImpl(a: Boolean?) : Type<Boolean> = Type.Boolean
fun <T>getImpl(a: T?) : Type<T> = error("Unsupported type")
fun main() {
println(getImpl(null as Int?)) // return Type.Int as expected
println(get<Int>()) // Same as above after get is inlined but throws!
}
Could it be that the overload is resolved before the method is inlined?
The goal is for some generic classes to take a Type<T> parameter and be guaranty that T is in the closed set. It also allows for testing the generic type T at runtime (workaround type erasure).
I would rather avoid having the clients specify Type.Int explicitly or have an implementation using unchecked cast such as:
inline fun<reified T> getUncheckedCast() : Type<T> =
when (T::class) {
Int::class -> Type.IntType as Type<T>
Boolean::class -> Type.BooleanType as Type<T>
else -> error("Unsupported type")
}
I think your last code block is the best solution. Although your get function is reified, the type is still generic so the compiler is going to resolve the overload as the generic one that throws an error. You can’t get the compiler to select which overload to call at runtime. It is always selected at compile time.
According to Kotlin documentation, the only difference reified parameter makes is that its runtime class is available:
4.5.2 Reified type parametersLoad tests
Type parameters of inline function declarations (and only those) can be declared reified using the corresponding keyword. A reified type parameter is a runtime-available type inside the function scope, see the corresponding section for details.
It doesn't specify that the type is substituted when the function is inlined.
This mean that reified is sugaring for implicitly passing the KClass of reified types:
inline fun <reified T>f() = T.class
// is desugared to
inline fun <T>f(__TKClass : KClass<T>) = __TKClass
Thus the overload resolution set is not affected by reifying types.

Type- or Class-keyed map

In Kotlin I can use filterIsInstance to obtain a type-specific (and type-safe) sub-collection:
val misc: List<Any> = listOf(42, 3.14, true, "foo", "bar")
val strings: List<String> = misc.filterIsInstance<String>()
println(strings) // => [foo, bar]
But I have a large collection of objects and I would like to pre-sort it into a Map, by concrete type. Is it even possible to define such a map in Kotlin's type system?
val miscByType: Map<KType, Collection<???>>
or:
val miscByClass: Map<KClass, Collection<???>>
Should I use a custom implementation with unsafe (but logically sound) casts?
The following is such an implementation. It works, but I'm wondering if there is a less hacky way of doing it:
import kotlin.reflect.KClass
class InstanceMap {
// INVARIANT: map from KClass to a set of objects of *that concrete class*
private val map: MutableMap<KClass<*>, MutableSet<Any>> = mutableMapOf()
// this is the only public mutator, it guarantees the invariant
fun add(item: Any): Boolean =
map.getOrPut(item::class) { mutableSetOf() }.add(item)
// public non-inline accessor, only needed by the inline accessor
fun get(cls: KClass<*>): Set<*>? = map[cls]
// inline accessor that performs an unsafe, but sound, cast
#Suppress("UNCHECKED_CAST")
inline fun <reified T> get(): Set<T> = get(T::class) as Set<T>? ?: setOf()
}
fun instanceMapOf(vararg items: Any): InstanceMap = InstanceMap().apply {
items.forEach { add(it) }
}
val misc = instanceMapOf(42, 3.14, true, "foo", "bar")
val strings = misc.get<String>()
println(strings) // => [foo, bar]
Your code looks OK. The only problem is with the unchecked cast warning. At the JVM bytecode level, the cast does nothing, because of the way, how generics are implemented in Java and Kotlin. It is also known as type erasure.
https://en.wikipedia.org/wiki/Type_erasure
Type erasure adds yet another problem to your code - it does not tell generic type arguments. So that, for example, List<Int> has the same class as List<String> or List<Map<String, Object>>
Do you expect your code to find superclasses or interfaces in the map? E.g. if I have
interface A
interface B
class C : A, B
val m = InstanceMap()
m.add(C())
m.get(C::class)
m.get(A::class)
m.get(B::class)
Do you expect these 3 calls to return the same value?
The JVM standard workaround for it is to explicitly pass Class<T> parameter and use the Class#cast method to cast instead. That change will make the code safer.
There is a remedy to type erasure in Kotlin. You may add a reified inline function so that Kotlin compiler will use the exact type in the inlined generic function body
https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters
inline fun <reified T> InstanceMap.get() = get(T::class)

why there is 'by' for the extended class and reified in function define

coming across a sample with a class and a function and trying to understand the koltin syntax there,
what does this IMeta by dataItem do? looked at https://kotlinlang.org/docs/reference/classes.html#classes and dont see how to use by in the derived class
why the reified is required in the inline fun <reified T> getDataItem()? If someone could give a sample to explain the reified?
class DerivedStreamItem(private val dataItem: IMeta, private val dataType: String?) :
IMeta by dataItem {
override fun getType(): String = dataType ?: dataItem.getType()
fun getData(): DerivedData? = getDataItem()
private inline fun <reified T> getDataItem(): T? = if (dataItem is T) dataItem else null
}
for the reference, copied the related defines here:
interface IMeta {
fun getType() : String
fun getUUIDId() : String
fun getDataId(): String?
}
class DerivedData : IMeta {
override fun getType(): String {
return "" // stub
}
override fun getUUIDId(): String {
return "" // stub
}
override fun getDataId(): String? {
return "" // stub
}
}
why the reified is required in the inline fun <reified T> getDataItem()? If someone could give a sample to explain the reified?
There is some good documentation on reified type parameters, but I'll try to boil it down a bit.
The reified keyword in Kotlin is used to get around the fact that the JVM uses type erasure for generic. That means at runtime whenever you refer to a generic type, the JVM has no idea what the actual type is. It is a compile-time thing only. So that T in your example... the JVM has no idea what it means (without reification, which I'll explain).
You'll notice in your example that you are also using the inline keyword. That tells Kotlin that rather than call a function when you reference it, to just insert the body of the function inline. This can be more efficient in certain situations. So, if Kotlin is already going to be copying the body of our function at compile time, why not just copy the class that T represents as well? This is where reified is used. This tells Kotlin to refer to the actual concrete type of T, and only works with inline functions.
If you were to remove the reified keyword from your example, you would get an error: "Cannot check for instance of erased type: T". By reifying this, Kotlin knows what actual type T is, letting us do this comparison (and the resulting smart cast) safely.
(Since you are asking two questions, I'm going to answer them separately)
The by keyword in Kolin is used for delegation. There are two kinds of delegation:
1) Implementation by Delegation (sometimes called Class Delegation)
This allows you to implement an interface and delegate calls to that interface to a concrete object. This is helpful if you want to extend an interface but not implement every single part of it. For example, we can extend List by delegating to it, and allowing our caller to give us an implementation of List
class ExtendedList(someList: List) : List by someList {
// Override anything from List that you need
// All other calls that would resolve to the List interface are
// delegated to someList
}
2) Property Delegation
This allows you to do similar work, but with properties. My favorite example is lazy, which lets you lazily define a property. Nothing is created until you reference the property, and the result is cached for quicker access in the future.
From the Kotlin documentation:
val lazyValue: String by lazy {
println("computed!")
"Hello"
}

How to check generic type in Kotlin?

I have class:
class Generic<T : SuperType>() { ... }
And this code is't correct, but cast s to type T:
fun typeCheck(s: SuperType) {
when(s) {
is T -> // Do something
}
}
If use: s as T - this cast will show warning (unsafe cast).
How check that s is T type?
If you need to check if something is of generic type T you need to to have an instance of Class<T> to check against. This is a common technique in Java however in Kotlin we can make use of an inlined factory method that gets us the class object.
class Generic<T : Any>(val klass: Class<T>) {
companion object {
inline operator fun <reified T : Any>invoke() = Generic(T::class.java)
}
fun checkType(t: Any) {
when {
klass.isAssignableFrom(t.javaClass) -> println("Correct type")
else -> println("Wrong type")
}
}
}
fun main(vararg args: String) {
Generic<String>().checkType("foo")
Generic<String>().checkType(1)
}
Generic types are not reified on the JVM at runtime, so there's no way to do this in Kotlin. The warning is correct because the compiler can't possibly generate any instruction that will fail when the cast is done, so the cast is unchecked, meaning that the program may or may not break at some point later instead.
A related feature which might be of use is reified type parameters in inline functions. Classes can't have reified type parameters though, so if you elaborate a bit more on your use case, I can try helping you achieve what you seem to need.
I know that I'm kinda late to this thread, but I just want to recap on the answer provided by Alexander Udalov.
It is, indeed, impossible to determine the type of a generic parameter in Kotlin unless you're using inline functions and declaring the generic type as reified.
Not sure if I'll be able to answer this question entirely and accurately, but I feel like my contribution might still be valuable for someone who is attempting to do just that. So let's say you have a few data classes, and you want to check which type you're dealing with.
You could use a function like that:
inline fun <reified T> checkType() = when (T::class) {
TypeA::class -> println("TypeA")
else -> println("Type not recognized")
}
however, functions that call it must also be inline, so you might have to write something like
inline fun <reified T> someOtherFunction(data: T) {
checkType<T>
}
however, if you cannot allow for an inline function (let's say in an interface!), you can kinda 'cheat' the system by saying, for example
class AmazingTypes {
inline fun <reified T> checkType(genericParameter: T) = when (T::class) {
TypeA::class -> println("TypeA")
else -> println("Type not recognized")
}
}
fun myAwesomeMethod(someParameter: Any) {
val amazingClass = AmazingClass()
amazingClass.checkType(someParameter)
}
This is also example.
inline fun <reified T: ApiResponse> parseJson(body: String): T {
// handle OkResponse only
val klass = T::class.java
if (klass.isAssignableFrom(OkResponse::class.java)) {
return T::class.java.newInstance()
}
// handle others
return gson.from(body, T::class.java)
}