Idiomatic Kotlin: Reified Parameters
This article is a part of the Idiomatic Kotlin series. The complete list is at the bottom of the article.
Today we will be talking about how Kotlin handles generics at runtime and what can we do about it.
Generics and Runtime
There was a time when Java did not support generics. Those were dark times. To support these legacy versions, generics was implemented using Type Erasure. In type erasure, type parameter information is removed at compile time and all parameters are replaced with their bound types if available or simply Object
.
Motivation
Aside from legacy interoperability, type erasure removes the need to create intermediate classes and object to store type information and remove the runtime overhead as the consequence of those.
Kotlin and Type Erasure
Kotlin, like Java, also implements type erasure. Let us look at an example.
As we said before, the type will be erased after compile time and the base type (if parameterized type) or the bounding type or Any
will be replaced. Let’s see it that is true.
The list is just a generic list with no information about the type of its elements.
Type erasure consequence
Since Type information was erased in runtime, you cannot therefore use the is
checking in parameterized classes. This is because in runtime, this is already replaced with just the base type. As an example, this will not compile.
You can check if it is a type of the base type but you will never get information about its type parameter. Checking for base type is done using star projection.
The same is true for the as?
cast. It will always succeed if the base type is the same but the type parameter cannot be guaranteed. Doing so will generate an unchecked cast warning.
Another thing to note is reflection on generic types. Since you have no access to the type at runtime, this makes reflection impossible.
Reification
Reification allows you to preserve the generic type in runtime. Kotlin supports reification of parameter types in a function under a certain condition, that being the function is an inline function. Let’s see why.
For us to access the type of the object from the above example, we have to declare the type parameter as reified and inline the function. Let’s see the decompiled code.
The doSomethingWithType
had the type erased as we expected. So we cannot use it in Java to preserve the type. The magic happened in the calling function. The function was inlined (as expected) and had the correct type substituted instead of an Object
type. This is possible because at the call-site, the compiler can infer the type of the object that is passed to the inline function.
Practical uses
Practical uses of the reification involves reflection, type checking and casting. It also allows you to work with the Class
type. I have used reification on one of my earlier tutorials to implement a simple event aggregator. You can check it here if you are interested.
Check out the other articles in the idiomatic kotlin series. The sample source code for each article can be found here in Github.
- Extension Functions
- Sealed Classes
- Infix Functions
- Class Delegation
- Local functions
- Object and Singleton
- Sequences
- Lambdas and SAM constructors
- Lambdas with Receiver and DSL
- Elvis operator
- Property Delegates and Lazy
- Higher-order functions and Function Types
- Inline functions
- Lambdas and Control Flows
- Reified Parameters
- Noinline and Crossinline
- Variance
- Annotations and Reflection
- Annotation Processor and Code Generation