How to Use the reified Keyword in Kotlin
- Generics Before Java 5
- Generics in Java 5
- What Is Type Erasure
- Passing a Class to a Generic Function
-
Use the
reified
Keyword With aninline
Function in Kotlin -
Overloading Functions With the Same Input Using
reified
in Kotlin - 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 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