Reification of the erased
The title might sound like a B-rated horror movie, but in reality, Kotlin’s “reified” keyword helps you do things that were not possible before. Generics provide type safety and help you avoid explicit type casts. Generics extend the type system to allow a type or method to operate on objects of various types while providing compile-time type safety. On the other hand, generics can be limiting when you need to access type info in a generic function, and the compiler tells you the info doesn’t exist!
This missing type info is a result of how generics are implemented in the JVM (hint: type erasure, which we’ll discuss later). As a workaround, you can access the deleted generic type by passing the class of the generic type as a parameter of the function.
This isn’t too bad, but as you know from other posts in the Kotlin Vocabulary series, Kotlin hates boilerplate code and aims to help you write less code! Kotlin addresses this problem with a unique keyword, reified, that lets you access the type info from within a generic function. If you are familiar with how generics work, you might be asking how this is even possible. Let’s see how :)
Before generics were added to Java with version 5.0, type info didn’t exist in collections. This means there is no indication if an
ArrayList is an
Integer, or any other object type.
Without generics, each time you want to access an object in a collection you need to perform an explicit cast. Plus, there is no guard against invalid casts which result in runtime exceptions.
To address this, generics were added in Java 5. With generics, you can define a specific type for a collection, and the compiler will warn you if you try to add any other type. Also, you don’t need to perform explicit casts, which might result in runtime exceptions.
Generics are implemented with a trick called type erasure. Since there was no type info before Java 5, the Java compiler first replaces all type info with a base
Object type and adds the necessary type casts. Type erasure allows both compile-time type safety by providing type information to the compiler as well as backwards compatibility by keeping the byte code the same as on previous Java versions. Meanwhile, type erasure can be limiting when you need the type info in a generic function.
Now let’s see how
reified manages to access type info at runtime that should have been erased at compile time. To tackle this problem, reified makes use of inline functions. If you’re unfamiliar with inline functions, read this blog post.
To recap, if a function is marked as inline, the Kotlin compiler will copy the function body to every place where it is used. One advantage of this is that the compiler is also free to modify the function body as it’s being copied over. To use reified parameter types, you first need to make the function inline and then add the
reified keyword to the generic parameter.
Let’s take a look at what is happening under the hood in the decompiled Java code. When there is a call to the inline function with a reified type, the compiler will copy the function body and replace generic types with the actual declared type. As a result, you don’t need to pass the class to access the type info.
Reified can be used only with inline functions, so the same rules that apply to inline functions also apply to reified. Also, keep in mind that reified functions can not be accessed from Java. Java doesn’t support inlining, and without inlining, generic parameters can not escape being erased by the compiler.
Reified also allows overloading functions to return generic types. For example, the following function can return an
Normally, a function can not be overloaded with the same input and have different return types. With inline functions, once again the compiler can replace the generic return type with the expected type while copying the function body. If you take a look at the decompiled Java Code, the compiler uses an
Integer type for the
intCall variable and uses
Float for the
Reified allows you to do things with generics which were previously not possible due to lack of type info at runtime. Use reified when you need the class type info in your inline functions or to overload generic return types. Reified doesn’t introduce any performance penalties, but inlining a large function can. Since a function needs to be inlined in order to use reified types, don’t forget to keep your reified functions short to avoid performance penalties and follow the best practices for inline functions.