KClass
is defined as public interface KClass<T : Any> : KDeclarationContainer, KAnnotatedElement, KClassifier
This is tricky, because the class of a String?
should be KClass<String>
, but is impossible to obtain.
Given the following 3 examples below (that should all do essentially the same work), 1 of them doesn't compile, and the others return the same runtime type.
inline fun <reified T> test1(): Any = T::class
inline fun <reified T: Any> test2(): KClass<T> = T::class
inline fun <reified T> test3(): KClass<T> = T::class // does not compile
test1<String?>() // class kotlin.String
test1<String>() // class kotlin.String
test2<String?>() // does not compile
test2<String>() // class kotlin.String
The point of the question is to ask: how can I get the runtime behavior of test1
with the compile time behavior (and safety) of test2
?
EDIT: one final addendum to the question is another example, which demonstrates the problem with getting the class of a nullable type.
inline fun <reified T> test4() {
val x = T::class // compiles, implied type of x is KClass<T>
val y: KClass<T> = T::class // does not compile with explicit type of KClass<T>
}
The call site that I am specifically having problems with is this:
class OutputContract<T>(
private val output: () -> T,
val outputType: KClass<T> // ERROR!
) {
fun invoke(): T {
return output()
}
}
inline fun <reified T> output(noinline output: () -> T): OutputContract<T> {
return OutputContract(output, T::class)
}
The only error here is with KClass<T>
, not with T::class
, which hums along just fine. I want to allow the consumer to specify nullability as part of the contract, so adding an Any
constraint will not work. If I just turn KClass<T>
into KClass<Any>
, this all works (which proves there is no runtime issue, only compile time). This is ultimately the workaround I have chosen, but it would be nice if I could actually maintain the proper type.