Returning concrete class that inherits generic class in Kotlin - kotlin

I’m trying to do the following:
inline fun <reified T> getMapper(klass: Class<T>): RFMFunction<T, Event> =
when (klass) {
MClick::class.java -> MClickMapper
else -> { throw RuntimeException("Unknown event type: $klass") }
}
with:
object MClickMapper : RFMFunction<MClick, Event>()
but Kotlin doesn’t recognize that MClickMapper is a subclass of RFMFunction<MClick, Event>.
I tried to add a cast as RFMFunction<MClick, Event> in the when, but that didn’t work.
The error I get is:
Type mismatch.
Required: RFMFunction<T, Event>
Found: ClickMapper
Is there a way to do that?
Thanks!

Kotlin doesn’t recognize that MClickMapper is a subclass of RFMFunction<MClick, Event>
That is not entirely correct. The error is Type mismatch: inferred type is MClickMapper but RFMFunction<T, Event> was expected.
So in fact, Kotlin refuses to consider MClickMapper as a subclass of RFMFunction<T, Event>, because T is provided by the caller of your function. The compiler doesn't go as far as knowing that klass == MClick::class.java means that T == MClick. You could technically use an unchecked cast here (since you know more than the compiler), but it should be a cast to RFMFunction<T, Event> (not RFMFunction<MClick, Event>). And then you can suppress the unchecked cast warning.
Also, as a side note, why do you use both a KClass and a reified type argument? If you reify T, you don't need the explicit KClass:
#Suppress("UNCHECKED_CAST")
inline fun <reified T> getMapper(): RFMFunction<T, Event> = when (T::class) {
MClick::class -> MClickMapper as RFMFunction<T, Event>
else -> throw RuntimeException("Unknown event type: ${T::class}")
}
However, this solution is not great, because you kinda hardcode twice the relationship between MClickMapper and MClick (in the when AND in the inheritance hierarchy), which means if those 2 places go out of sync you can have problems that won't be seen at compile time. It would be better to use the type information to create the mapping dynamically, so you have the mapping harcoded in a single place.

Related

Reified inline function in Kotlin still leading to compiler error on 'is' check

I have the following function:
inline fun <reified T> create(preference: Preference<T>, title: String = ""): DebugOption{
val type = when (preference) {
is Preference<String> -> Type.STRING
is Preference<Boolean> -> Type.BOOLEAN
else -> Type.STRING
}
return DebugOption(type, preference, displayTitle = StringModel(title))
}
I was expecting to be able to easily perform this 'is' check, since the generic type is reified, but I am still getting a compiler error:
Cannot check for instance of erased type: Preference<String>
Cannot check for instance of erased type: Preference<Boolean>
So I am confused how am I misusing 'reified' / what am I missing here.
Is there a problem with using reified generic types as the type parameter of another class?
The problem is that is checks the runtime type of preference, and the runtime type of preference isn't available until all the generic type information has been erased. Reified types are not magic, after all.
What you can do instead, is check T instead, since as you said, T is indeed reified.
val type = when (T::class) {
String::class -> Type.STRING
Boolean::class -> Type.BOOLEAN
else -> Type.STRING
}
But note that if Preference is covariant (like List) or contravariant, this might not work as you expect in some cases. For example:
// suppose Preference is covariant
// (i.e. the type parameter is declared <out T>)
val pref: Preference<Any> = Preference<Boolean>()
create(pref, "foo")
In this case, T is inferred to be Any, so type will be assigned Type.STRING. If that is unexpected to you, and you want Type.BOOLEAN instead, you might want to use another way to check a preference's type, rather than reified types, as this cannot be determined at compile time.

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.

Why can't Kotlin auto cast generic type

If you are familiar with unity3d, I'm trying to implement a similar pattern for unity components:
AddComponent<T>();
GetComponent<T>();
So I made this snippet in kotlin:
val map = mutableMapOf<Class<Any>,IComponent>()
fun <T : IComponent> addComponent(component : T){
map.put(component.javaClass,component)
}
fun <T : IComponent> getComponent(klazz : Class<T>): T {
return map.get(klazz)
}
First of all, I have to pass a class to the getComponent method, I can't infer the type from T like C# and I was wondering if there is a way to do this.
And most importantly, why is the method giving me a compile error saying the I'm returning IComponent where T is required, although I did say that T IS an IComponent?
I have to cast to T which is unsafe, this works perfectly fine in C# universe but I'm new to kotlin and I'm wondering if that's possible.
First of all, I have to pass a class to the getComponent method, I can't infer the type from T like C# and I was wondering if there is a way to do this
To be able do something like this the type have to be reified.
In your case it would be something like this:
inline fun <reified T : IComponent> getComponent(): T {
val klazz = T::class.java
// something-something that returns T
}
and most importantly, why is the method giving me a compile error saying the I'm returning IComponent where T is required, although I did say that T IS an IComponent
You said that the type T is IComponent, but not that IComponent is T. And the map contains IComponent as values. Some of them can be T, but there're no guarantee for compiler that they are. So compiler falls with error, and says it isn't sure you will get something of type T out of the map.
So you need to force cast result to the type:
return map.get(klazz) as T // in your case you will have to cast klazz to Class<Any>, btw
To make it castles you should define map as
val map = mutableMapOf<Class<*>,IComponent>()
instead of
val map = mutableMapOf<Class<Any>,IComponent>()
Also, it would be better to use optional type T? for getComponent, in pair with conditional cast as?.

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 get generic param class in 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.