Type projection clash when accessing property delegate instance


#1

My task is to search given class for delegated properties and access information stored in delegate instances. I can access the list of all properties via reflection and then check each one for its type. The problem is that code like this:

        target::class.memberProperties.forEach {
            val delegate = it.getDelegate(target)
        }

throws an exception since memberProperties produces out projection and then getDelegate can’t consume initial target. Of course, I can manually cast resulting property to KProperty1<Any, Any?>, but it looks really ugly and requires unchecked cast.

Is there a better solution for this?


Kotlin Reflection and Variance
#2

I don’ t think there is. The real question is why this is a out projection in the first place. I can’t find it in the definition of memberProperties.

val <T : Any> KClass<T>.memberProperties: Collection<KProperty1<T, *>>

#3

It seems that projection appears in target::class. I don’t know, why. Also I don’t know, why Any does not fit out Any. I am working with koltin for a year and this one still eludes me.


#4

Interesting. I wonder what the reason for this is.
Another interesting feature in regard to this I just found is this

@Suppress("unused")
inline fun <reified T : Any> T.kclass() = T::class

class Foo
val foo = Foo()
foo::class // KClass<out Foo>
foo.kclass // KClass<Foo>
Foo::class // KClass<Foo>

So it appears that the result of ::class changes when used on an object instead of a type.


#5

Maybe there is a reason behind it, but searching for a list of specific properties via reflections seems to be common use case. There should be a way to do so without unsafe casts.


#6

This looks like https://youtrack.jetbrains.com/issue/KT-16432 problem.


#7

The problem is that your foo object may be of a child type. Some of the declared properties will thus be only declared on the child (rather than the Foo class). Unfortunately even if you have a helper function that takes a T (for the foo value), getting the class out of it will still be out projected (you could have manually specified a supertype as type parameter).


#8

It seems so. I see, there is not clear solution for that problem using classes, but is always possible to get list of members on class instance (object) since all of the properties should be present for instantiation. Looks like it is easier to add a number of utility methods to get properties and methods directly on object like Reflect.getProperties(obj: Any).