Home > OS >  Generic Parameter Requires type "Nothing" when T is type "Any"
Generic Parameter Requires type "Nothing" when T is type "Any"

Time:02-18

I am a new Kotlin programmer, and I am having a problem relating to generics

In the following code, I am getting an error with it.get(this) "Type mismatch. Required: Nothing. Found: Any.

inline fun <reified T : Any> Any.getPropertiesWithType() =
    this::class.memberProperties
        .filter { it.returnType.isSubtypeOf(T::class.starProjectedType) }
        .map {
            it.isAccessible = true
            it.get(this) as T
        }

This is confusing because memberProperties returns a Collection<KProperty1<T, *>> where T is the type of this, which is Any in my case. KProperty1.get() takes one argument of that same type T, so I would expect Any could be passed without an issue.

One thing I noticed is that it in the filter and map are both type KProperty<out Any, *>> but memberProperties does not have the out variance.

If I replace this::class with this.javaClass.kotlin, it works without an error, but that seems like a very bad way to go about this.

If anyone knows a way to fix this, or an entirely different strategy, it would be much appreciated. I am new to Kotlin and still do things in the Java way sometimes.

CodePudding user response:

this::class is analogous to this.getClass() in Java, which returns a KClass<out Any> (Class<? extends Object> in Java). After all, just because the type of this is Any, its runtime type may not be Any - it could be any subclass of Any. out Any captures this idea.

Therefore, the memberProperties of this::class will be a collection of KProperty1<out Any, *>.

In the same way that you cannot add anything to a MutableList<out Any> using add, you cannot get the value of a KProperty1<out Any, *> - an input parameter's type is marked out. Another way to think about this is that the property could be on any subclass of Any, trying to get the property using this, which is just an Any, obviously doesn't work.

I would move the unchecked cast to it:

(it as KProperty1<Any, T>).get(this)

As for why .javaClass works, it is because javaClass actually gives you a Class<Any>, not Class<out Any>. IMO, this doesn't make much sense, but it is what it is.

  • Related