Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to obtain a KType in Kotlin?

I'm experimenting with the reflection functionality in Kotlin, but I can't seem to understand how to obtain a KType value.

Suppose I have a class that maps phrases to object factories. In case of ambiguity, the user can supply a type parameter that narrows the search to only factories that return that type of object (or some sub-type).

fun mapToFactory(phrase: Phrase,
          type: KType = Any::class): Any {...}

type needs to accept just about anything, including Int, which from my experience seems to be treated somewhat specially. By default, it should be something like Any, which means "do not exclude any factories".

How do I assign a default value (or any value) to type?

like image 774
user1582024 Avatar asked Mar 17 '17 16:03

user1582024


2 Answers

Since Kotlin 1.3.40, you can use the experimental function typeOf<T>() to obtain the KType of any type:

val int: KType = typeOf<Int>()

In contrast to T::class.createType(), this supports nested generic arguments:

val listOfString: KType = typeOf<List<String>>()

The typeOf<T>() function is particularly useful when you want to obtain a KType from a reified type parameter:

inline fun <reified T> printType() {
    val type = typeOf<T>()
    println(type.toString())
}

Example usage:

fun main(args: Array<String>) {
    printType<Map<Int, String>>()
    // prints:   kotlin.collections.Map<kotlin.Int, kotlin.String>
}

Since this feature is still in experimental status, you need to opt-in with @UseExperimental(ExperimentalStdlibApi::class) around your function that uses typeOf<T>(). As the feature becomes more stable (possibly in Kotlin 1.4), this can be omitted. Also, at this time it is only available for Kotlin/JVM, not Kotlin/Native or Kotlin/JS.

See also:

  • Release announcement
  • API Doc (very sparse currently)
like image 127
TheOperator Avatar answered Sep 28 '22 10:09

TheOperator


From your description, sounds like your function should take a KClass parameter, not a KType, and check the incoming objects with isSubclass, not isSubtype.

Types (represented by KType in kotlin-reflect) usually come from signatures of declarations in your code; they denote a broad set of values which functions take as parameters or return. A type consists of the class, generic arguments to that class, and nullability. The problem with types at runtime on JVM is that because of erasure, it's impossible to determine the exact type of a variable of a generic class. For example if you have a list, you cannot determine the generic type of that list at runtime, i.e. you cannot differentiate between List<String> and List<Throwable>.

To answer your initial question though, you can create a KType out of a KClass with createType():

val type: KType = Any::class.createType()

Note that if the class is generic, you need to pass type projections of generic arguments. In simple cases (all type variables can be replaced with star projections), starProjectedType will also work. For more info on createType and starProjectedType, see this answer.

like image 44
Alexander Udalov Avatar answered Sep 28 '22 08:09

Alexander Udalov