Declare the generic parameter of the function type in a way that allows both for types with a null value and using this type parameter to declare `KClass <T>`

KClass defined as public interface KClass<T : Any> : KDeclarationContainer, KAnnotatedElement, KClassifier

This is complicated because there String?must be a class KClass<String>, but it cannot be obtained.

Given the following three examples below (they should all do the same job), 1 of them does not compile, and the rest return the same type of runtime.

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 goal of the question is to ask: how can I get runtime test1behavior with compile-time behavior (and security) test2?

EDIT: One last addition to the question is another example that demonstrates the problem of getting a class of type NULL.

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>
}

, , :

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)
}

KClass<T>, T::class, . , Any . KClass<T> KClass<Any>, ( , , ). , , , , , .

+4
2

. myFun<String?>(), , , , , . , , . , , : " ?"

, T? , , . : T: Any , , , .

, :

  • , , , NULL, reified :

    // call site, any return type nullable or not
    val something: String? = doSomething()
    
    // function
    inline fun <reified T: Any> doSomething(): T? {
        val x: KClass<T> = T::class
        // ...
    }
    
  • , :

    // call site, any parameter type nullable or not
    val param: String? = "howdy"
    doSomethingElse(param)
    
    // function
    inline fun <reified T: Any> doSomethingElse(parm: T?) {
        val x: KClass<T> = T::class
        // ...
    }
    
  • ( ):

    // call site, any non-nullable generic parameter
    doSomething<String>()
    
    // function
    inline fun <reified T: Any> doSomethingElse() {
        val x: KClass<T> = T::class
        // ...
    }
    
  • , ( ????):

    // call site: whatever you want it to be
    
    // function:
    inline fun <reified T> test4() {
        val x = T::class // compiles, implied type of x is KClass<T>
        val y: KClass<*> = T::class  KClass<T>
    }
    

    x y , / KClass.

, , , . , T? , ?

<T: Any> T? .

, NULL, output, KClass:

class OutputContract<T: Any>(private val output: () -> T?, val outputType: KClass<T>) {
    fun invoke(): T? {
        return output()
    }
}

inline fun <reified T: Any> output(noinline output: () -> T?): OutputContract<T> {
    return OutputContract(output, T::class)
}

- nullability, , null, Kotlin . invoke , . , , T, KClass, KClass<*> , KClass. , . , KClass, . KClass , , , KType.

Image from Understanding Generics and Variance in Kotlin

+1

: , , , . , "" .

Nullability KType KClass, KType . , KT-15992. , , .

reified KClass, , KClass , .

, , KType, , T::class.createType(...), . reified T, , :

inline fun <reified T : Any?> sample() {
    println("Is nullable: ${isNullable<T>()}")
}

inline fun <reified T : Any?> isNullable(): Boolean {
    return null is T
}

fun main(args: Array<String>) {
    sample<String?>()
    sample<String>()
}

// Is nullable: true
// Is nullable: false

, KType.


:

KType . Klutter, Klutter 2.5.3, .

, , , :

val nullableString = reifiedKType<String?>()
println(nullableString.isMarkedNullable) // true

val nonNullableString = reifiedKType<String>()
println(nonNullableString.isMarkedNullable) // false
+2

Source: https://habr.com/ru/post/1695673/


All Articles