Today let’s set up the kotlin-reflect dependency and look at some jvm Java/Kotlin reflection. You might set up a small project for that, or have a look to the examples.

Make sure your gradle is set up with the right dependency:

dependencies {
    implementation(kotlin("stdlib"))
    implementation("org.jetbrains.kotlin:kotlin-reflect")
}

With Kotlin v1.4.0 or older, so you can have access to the latest features (like typeOf)!

But before we dig into the world of kotlin reflection, let’s have a look at some common classes you may have encountered in your Kotlin journey. We’ll then see how we can use reflection to bring those classes to instances.

Kotlin

String

The String is a very basic class:

public class String : Comparable<String>, CharSequence { .. }

As its class definition states, it is a character string like "abc", one very basic object that also exist under java.lang.String and can be referred as a primitive type.

Once created a String is immutable (for many practical reasons), when you think you change it, a new String is in fact created.

Collections

List

Due to generic type erasure List class has a single implementation for all its generic instantiations. That’s why you can’t List<String>::class because the type is not reified.

public interface List<out E> : Collection<E>

Although the interface is defined in kotlin.collections.List, the actual implementation of lists in Kotlin relies on ArrayList. Meaning that you have:

listOf("A", "B")::class // class java.util.Arrays$ArrayList
List::class             // class kotlin.collections.List

The $ is used by the jvm for generated sources as per the JLS (Java Language Specification).

EmptyList

EmptyList is an internal object of the Kotlin Collection implementation, It is returned when you call listOf() with no elements, it goes in the background and call the emptyList method:

@kotlin.internal.InlineOnly
public inline fun <T> listOf(): List<T> = emptyList()

EmptyList is in CollectionsKt, which returns a list of Nothing to represent a value that never exists. It has no instance:

internal object EmptyList : List<Nothing>, Serializable, RandomAccess { .. }

It’s an immutable object so its use might be limited. Since it’s from Kotlin internal, EmptyList type does not exist in java.

Arrays

arrayOf()

One difference between Array and List, is that you can do Array<String>::class because the type is kept. When creating an Array in Kotlin, the first reflex is arrayOf one of Kotlin useful built-in:

public inline fun <reified @PureReifiable T> arrayOf(vararg elements: T): Array<T>

Here you can see that T is prefixed with the reified word. The reified type parameter means that you have access to the class of T at compile-time and can access it like T::class. That’s why you often see “UNCHECKED_CAST” warning around arrays, because the generic T is considered as a class in the Array<T>::class notation.

In opposition to EmptyList, there are no EmptyArray because Array is based on the java type java.lang.String[].

Array﹤T﹥

The Array’s constructor takes two parameters, the size and an init lambda function that return the value for an array element its given index

public class Array<T> {
    public inline constructor(size: Int, init: (Int) -> T)
    // ...implementation details
}

Let’s give it a try and instantiate our own Array. We’ll set the size to 3 and have an init function such as i: Int -> i:

val array: Array<Int> = Array(3) { i: Int -> i } 
// which gives [0, 1, 2]

We realize that this init function we’ve chosen is just returning the index and that’s it. Lame. Though with other lambda function you could get creative and have some interesting array. Using reflection to call a constructor like that is not an easy fit, even with arguments that should be working:

assertThrows<Error> { 
    Array<Int>::class.constructors.first().call(3, { i: Int -> i }) 
}

I didn’t see more than one constructor in the Array class, so I expected to use it like we did before to instantiate one by reflection. However, this throw a kotlin.reflect.jvm.internal.KotlinReflectionInternalError!

Reflection

KClass

A KClass is a kotlin class that you get using for example List::class during runtime. It is the most basic reflection feature.

val k: KClass<*> = String::class
val j: Class<*> = String::class.java

The java Class and the kotlin KClass are not the same.

Ktype

A KType represents a type, it is an important feature for reflection with generic types (e.g. T). A type can be:

  • An actual class with optional type arguments (The type of String is the class String)
  • A type parameter of some declaration (Like a List<T> where T is a generic type)
  • Nullable or non-nullable (Both T and T? are accepted)
private fun <T> nullableType(): List<T?> {
    return listOf()
}

We talked about reified type, where you can’t do List<String>::class. However, you can do typeOf<List<String>>() to get the KType.

val kt: KType = String::class.createType()
val jt: Type = String::class.createType().javaType

The java Type and the kotlin KType are not the same.

KClassifier

The KClassifier is what the type is based on which can be either a class or a type parameter. On the type, you have access to the KClassifier using:

val ks: KClassifier? = typeOf<String>().classifier

In this case it returns String but for the type List<String> the classifier would return the List KClass.

Conclusion and Examples

Now that we went through the reflection basics with KClass, KType and KClassifiers for the Kotlin Classes described earlier. Let’s have some examples for the three Kotlin objects presented earlier. Also for the demonstration, the java class and type (::class.java.typeName) are obtained with the kotlin reflection engine.

String

For String, like most simple objects, it stays basic:

  • KType: kotlin.String
  • KClass: class kotlin.String
  • KClassifier: class kotlin.String
  • Java Type: java.lang.String
  • Java Class: class java.lang.String

That is what you would except when no generics are involved.

List

For a List, like a list of String. You see that not everything is kept at runtime:

  • KType: kotlin.collections.List<kotlin.String>
  • KClass: class java.util.Arrays$ArrayList (using an instantiated list)
    • It’s not kotlin.collections.List because it uses ArrayList internally
    • The String is not in the class definition
  • KClassifier: class kotlin.collections.List
    • It is not map to an actual class (kotlin.collections.List is an interface)
  • Java Type: java.util.ArrayList
  • Java Class: class java.util.Arrays$ArrayList
    • Like the KClass (which is not common).

Kotlin specific internal classes do not have corresponding java classes

In the case of EmptyList the java class does not exist and is replaced with class kotlin.reflect.jvm.internal.KClassImpl. In this case creating a new instance will throw a kotlin reflect internal error.

Array

For an Array of String, its behaviour is different from a list:

  • KType: kotlin.Array<kotlin.String>
    • With argument type typeOf<Array<String>>().arguments[0].type!! as kotlin.String
  • KClass: class kotlin.Array
  • KClassifier: class kotlin.Array
  • Java Class: class [Ljava.lang.String;
    • Arrays are dynamically created and may be assigned to variables of type Object (as superclass).
  • Java Type: java.lang.String[]
    • In java the array’s type is written T[] i.e. “String” in this case.

Because Arrays are dynamically generated, the reflection and cast can be tricky because it will be converted to an Array of object first java.lang.Object[], then you need to cast to the right type. To remedy we have java.lang.reflect.Array in the java reflection engine to make new instance from the type:

@Suppress("UNCHECKED_CAST")
private inline fun <reified T> createArrayOfGeneric(): Array<T> {
    return java.lang.reflect.Array.newInstance(typeOf<T>().javaType as Class<*>, 10) as Array<T>
}

The other Kotlin alternatives that I tried throw ClassCastException when trying to cast a [Ljava.lang.Object; to a [Ljava.lang.String; as shown in the example. If you liked it, 🎉 throw an upvote on the answer on stackoverflow 🙃