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

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.

Related

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?.

Kotlin: Generic type functions: Out-projected type ... prohibits the use of

Given a class:
class Data<T>{
fun get(): T = Something as T
fun update(item: T) { }
}
How to do something like this?
fun alterData(d: Data<*>){
d.update(d.get())
}
The function "alterData" won't compile:
Out-projected type 'Data<*>' prohibits the use of 'fun update(item: T)'
Update:
The caller of alterData() don't know the type, so this is not possible:
fun <T>alterData(d: Data<T>){
d.update(d.get())
}
The only way I found to bypass this limitation is by means of writing the method inside the class, but that's breaks abstraction
* basically means that you don't know what the type T is in this case, so it could be anything.
Because you have defined d as Data<*> the call d.get() returns Any?. The compiler cannot know what type of data d contains. Therefor you have to expect anything that's valid for T, including null.
Because you have defined d as Data<*> the call d.update(…) accepts only Nothing, i.e. no value at all. The compiler cannot know what type of data d is allowed to receive. Therefor it cannot accept anything as it may or may not be valid here.
What you want to tell the compiler is that there is a relationship between what comes out of d (out projection) and what goes into d (in projection):
fun <T> alterData(d: Data<T>) {
d.update(d.get())
}
Now the compiler knows that whatever comes out of d (through .get()) is of the same type as what you put into d (through .update(…)). In both cases that type is referred to as T.
Note that the "type parameter" T doesn't have to have same name as your Data<T> declaration. The following is equally valid to indicate the relationship:
fun <Something> alterData(d: Data<Something>) {
d.update(d.get())
}
It may be easier to understand if you break the code down further:
fun alterData(d: Data<*>) {
// `value` is of type `Any?` because we don't know `T` of `Data`
val value = d.get()
// `update` rejects `Any?` b/c we don't know `T` and it may not be valid
d.update(value)
}
fun <Something> alterData(d: Data<Something>) {
// `value` is of type `Something` b/c we know that `T` of `Data` is `Something`
val value = d.get()
// `update` accepts `Something` b/c we know `T` is `Something`
d.update(value)
}
As in your case neither the caller nor the callee (alterData) know the actual type of T you"ll have to tell the compiler to ignore compile-time type safety by using a cast.
You can do that either in the caller or the callee. Where it makes more sense depends on the actual use case. Usually the caller knows a little more about the context and the possible type of T than the callee, so it makes sense to perform a cast there.
fun foo(d: Data<*>) {
// ignore all type safety for `T`
alterData(d as Data<Any?>)
}
The callee alterData immediately passes a value that it receives from one Data instance (d) back to the same instance. In that case it's safe to assume that that can never go wrong, given your definition of class Data. If that's the case then you can safely perform the case in the alterData function:
fun alterData(d: Data<*>){
// ignore type safety and allow any value to be passed into `d`
(d as Data<Any?>).update(d.get())
}
Both approaches will raise a compiler warning that you could ignore using #Suppress("UNCHECKED_CAST").

Non-null type in nullable references

I have a class for binding a viewholder
class ViewHolderBinder(val onBind: (ViewHolder) -> Unit, val onClick: () -> Unit)
There is a list of these items in presenter
val items: MutableList<ViewHolderBinder> = mutableListOf()
ViewHolderBinder contains 2 non-null functions, but this call causes compile-time error
items.getOrNull(position)?.onClick()
However this call compiles as expected
items.getOrNull(position)?.let { it.onClick() }
Maybe i have missed something, but 2 these constructions are fully equivalent and i prefer to use first one, but it is not compiling.
I am using kotlin 1.3.10
Here's the actual error message you're getting:
Reference has a nullable type '(() -> Unit)?', use explicit '?.invoke()' to make a function-like call instead
Suppose you had this code:
val binder: ViewHolderBinder? = getBinder()
binder?.onClick()
onClick() is not a function you can invoke on the ViewHolderBinder instance. It's a property that holds a callback object. The full syntax to invoke that callback's function is
binder?.onClick?.invoke()
Kotlin also offers a special shorthand syntax that would work on a non-nullable binder:
binder.onClick()
If you apply it to a nullable binder,
binder?.onClick()
it expands to
binder?.onClick.invoke()
The type of the expression binder?.onClick is (() -> Unit)?, just like the error says. You aren't allowed to apply the . operator to a nullable type.

Difference between Any type and Generics in Kotlin

Suppose I have the following function definition.
fun<T> parse(a: Any): T = when (a) {
is String -> a
else -> false
}
I guessed it should be valid. However, the IntelliJ IDEA linter shows a type mismatch error
That being said, I would change the return type of my parse function to Any, right? So that, what is the difference between using Any type and Generics in Kotlin? In which cases should use each of those?
I did read the following question but not understood at all about star-projection in Kotlin due to the fact I am quite new.
Your return type it defined as T, but there is nothing assuring that T and a:Any are related. T may be more restrictive than Any, in which case you can't return a boolean or whatever you provided for a.
The following will work, by changing the return type from T to Any:
fun<T> parse(a: Any): Any = when (a) {
is String -> a
else -> false
}
Any alternate option, if you really want to return type T:
inline fun<reified T> parse(a: Any): T? = when (a) {
is T -> a
else -> null
}
Your example does not use T and thus it's nonsense to make it generic anyways.
Think about this: As a client you put something into a function, e.g. an XML-ByteArray which the function is supposed to parse into an Object. Calling the function you do not want to have it return Any (Casting sucks) but want the function return the type of the parsed object. THIS can be achieved with generics:
fun <T> parse(xml: ByteArray): T {
val ctx: JAXBContext = JAXBContext.newInstance()
val any = ctx.createUnmarshaller().unmarshal(ByteArrayInputStream(xml))
return any as T
}
val int = parse<Int>("123".toByteArray())
val string = parse<String>("123".toByteArray())
Look at the method calls: You tell with generics what type is expected to be returned. The code is not useful and only supposed to give you an idea of generics.
I guessed it should be valid
Why would it be? You return a String in one branch and a Boolean in the other. So the common type for the entire when expression is Any and that's what the compiler (and IDEA) says is "found". Your code also says it should be T (which is "required").
Your generic method should work for any T, e.g. for Int, but Any isn't a subtype of Int and so the code isn't valid.
So that, what is the difference between using Any type and Generics in Kotlin?
This is like asking "what is the difference between using numbers and files": they don't have much in common in the first place. You use generics to write code which can work with all types T (or with all types satisfying some constraint); you use Any when you want the specific type Any.