Why the reference to a class of a specific object is bounded?

The following program does NOT compile

val myString = "Hello World" // typed String
val stringKClass = myString::class // typed KClass<out String>
val stringLength = stringKClass.memberProperties.first { it.name == "length" } // typed KProperty1<out String, *>
val myStringLength: Any? = stringLength.get(myString) // does not compile

myString::class is typed KClass<out String>, I would expect KClass<String> with no upper bound on the type parameter. This syntax is explained in the documentation in a section named bound-class-references, so it is probably done on purpose. But I cannot understand the reason behind.

Morever, I can workaround my issue by using Java interop:

val stringKClass = myString.javaClass.kotlin // typed KClass<String>

Why the typing of myString::class and myString.javaClass.kotlin are different?

1 Like

Unlike ClassName::class, which always yields the KClass representing ClassName, using ::class on an object will yield the KClass representing the runtime type of the object, which may be a subclass of its static type. This is why the expression has an out modifier in its type. This is also consistent with java.lang.Object.getClass:

The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called. For example, no cast is required in this code fragment:

Number n = 0;
Class<? extends Number> c = n.getClass(); 

Now, imagine if object::class had type KClass<C> without the out modifier:

val myString: Any = "Hello World" // Notice the explicit type
val kClass = myString::class // Assume this was typed KClass<Any> rather than KClass<out Any>
val stringLength = kClass.memberProperties.first { it.name == "length" } // Then this would have type KProperty1<Any, *>
stringLength.get(42) // And this would compile and fail at runtime

As you noticed yourself, the .javaClass extension property returns Class<T> rather than Class<out T>. I think this is an error.

4 Likes

Thank you for your explanation. Now it is clear.