Use the reified Keyword in Kotlin

Use the reified Keyword in Kotlin

  1. Generics Before Java 5
  2. Generics in Java 5
  3. What Is Type Erasure
  4. Passing a Class to a Generic Function
  5. Use the reified Keyword With an inline Function in Kotlin
  6. Overloading Functions With the Same Input Using reified in Kotlin
  7. Conclusion

The reified keyword is a programming concept mostly used while working with generics in Kotlin.

We know that generic type was introduced in Java 5 to add type safety to applications and prevent explicit type casts.

One limitation with generics is that you cannot access type information when working with generic functions. The compiler complains that it cannot use T as a reified type parameter, and we should use a class instead.

This issue is caused by type erasure during compilation. In this tutorial, we will learn how we can work around this issue using two approaches, including passing the class of the type as the argument of the generic function and using the reified keyword with an inline function.

Generics Before Java 5

Before Java 5, generics did not exist, so we could not tell whether a list implementation was a list of String, Integer, Objects, or other types.

Due to this reason, we always had to cast to the type we wanted explicitly. These applications were prone to RuntimeException as there were no validations against invalid casts.

The following example shows how collections were implemented before Java 5.

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("John");
        list.add(3);
        list.add(5);

        String john = (String) list.get(2);
    }
}

Generics in Java 5

Generics were introduced in Java 5 to solve this problem. Generics allowed us to define a certain type of collection, and if we try to enter an invalid type, the compiler shows a warning.

Generics also solved the issue of RuntimeException because no explicit type casts were required to retrieve an element from the collection. The following example shows how collections were implemented from Java 5 and earlier versions.

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("John");
//        list.add(3); // Required type String
//        list.add(true); // Required type String

        String john = list.get(0);
        System.out.println(john);

    }
}

What Is Type Erasure

Type erasure is the feature that was introduced in Java 5 to solve the issue we have just mentioned above.

Go to IntelliJ and select File > New > Project. Enter the project name as reified-in-kotlin on the project name section.

Select Kotlin on the Language section and Intellij on the Build system section. Finally, press the Create button to generate a new project.

Create a Main.kt file under the folder kotlin and copy and paste the following code into the file.

fun <T> showType(t: T){
    println(t);
}

fun main(){
    showType("This is a generic function");
}

Before Java 5, there was no type information, so the Java compiler replaced the type information with the base object type and its necessary type cast.

To view what happens behind the hood in Java, run the code and press Tools > Kotlin > Show Kotlin Bytecode. On the window that opens, press Decompile to view the compiled Java code shown below.

public final class MainKt {
   public static final void showType(Object t) {
      System.out.println(t);
   }

   public static final void main() {
      showType("This is a generic function");
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
}

Note that the type parameter that we passed to the showType() methods get converted to an Object, which is why we cannot access type information. This is how type erasure occurs while working with generics.

Passing a Class to a Generic Function

This is the first approach we can use to avoid type erasure though it is not as efficient as using the reified keyword, which we will cover in the next section.

To access the deleted generic type, we can pass the generic type class as a parameter of the generic function, as shown below.

Comment on the previous example and copy and paste the following code into the Main.kt file.

fun <T> showType(clazz: Class<T>){
    println(clazz);
}
fun main(){
    showType(String::class.java)
}

With this approach, we will need to pass a class for the generic type every time we create a generic function, which is not what we want.

Run this code and ensure that the code outputs the following.

class java.lang.String

Use the reified Keyword With an inline Function in Kotlin

This is the second approach we can leverage and is the most preferred method when accessing type information using generics. Note that reified can only be used with inline functions, which are available in Kotlin but not in Java.

Comment on the previous example and copy and paste the following code into the Main.kt file.

inline fun < reified T> showType(){
    println(T::class.java);
}
fun main(){
    showType<String>()
}

The inline function helps the reified keyword to access the type information, copying the inline function body to every place it has been used.

Run the above code and decompile it using the same steps we covered above. The decompiled code is shown below.

public final class MainKt {
   // $FF: synthetic method
   public static final void showType() {
      int $i$f$showType = 0;
      Intrinsics.reifiedOperationMarker(4, "T");
      Class var1 = Object.class;
      System.out.println(var1);
   }

   public static final void main() {
      int $i$f$showType = false;
      Class var1 = String.class;
      System.out.println(var1);
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
}

Since we defined the inline function showType() with the reified keyword in the generic parameter, the compiler copied the function’s body and replaced it with the actual declared type, as shown in the final method named main().

Access to the declared types allows us to retrieve type information from the generic function without passing the class as a parameter.

Run the above code to verify that it can access the type information of String which is the type parameter we passed as an argument.

class java.lang.String

Overloading Functions With the Same Input Using reified in Kotlin

While working with normal generic functions, we cannot overload a function with the same input and return different types, but we can achieve this using the reified keyword.

Comment on the previous example and copy and paste the following code into the file after the Main.kt file.

inline fun <reified T>computeResult(theNumber: Float): T{
    return when(T::class){
        Float::class -> theNumber as T
        Int::class -> theNumber.toInt() as T
        else -> throw IllegalStateException("")
    }
}
fun main(){
    val intResult: Int = computeResult(123643F);
    println(intResult);
    val floatResult: Float = computeResult(123643F);
    println(floatResult);
}

Behind the scenes, when we invoke the computeResult() method, the compiler expects an Int return type on the first call and a Float return type on the second call.

The compiler uses this information to replace the generic type with the expected file while copying the function’s body.

It is not a good practice to use large functions while using reified as it introduces performance issues in our applications. Ensure your inline function is small.

Conclusion

So, we have learned to use the reified keyword with inline functions to access type information in Kotlin.

We have also learned about type erasure, which is the cause of the runtime deletion of type information. Lastly, we covered how to overload inline functions using reified with the same input to return different types.

David Mbochi Njonge avatar David Mbochi Njonge avatar

David is a back end developer with a major in computer science. He loves to solve problems using technology, learning new things, and making new friends. David is currently a technical writer who enjoys making hard concepts easier for other developers to understand and his work has been published on multiple sites.

LinkedIn GitHub