Understanding reified in Kotlin: A Comprehensive Guide

In this article, we'll explore the concept of reified in Kotlin, its importance, and practical uses. We'll demonstrate how to effectively use reified with examples, compare scenarios before and after its application, and discuss key considerations and best practices. By the end, you'll have a solid understanding of reified and how to apply it in your Kotlin projects.

 

Table of Contents

What is reified?

Reified is a keyword in kotlin which is used with inline functions to retain type information at runtime. 

Why do we need it?

Due to Type Erasure Problem

We need reified because Kotlin (like Java) erases generic type information at compile time. This is known as type erasure. So, when a type parameter is reified, the type information is preserved at runtime. This allows us to perform type checks and casts that would normally be impossible with erased types. 

Let’s look at an example to understand it better.

Without reified

fun <T> someFunction(value: T) {
    println(“The value is: $value”)  Successful
    println(“Type is: ${T::class.simpleName}")   // Error
}

fun main() {
    someFunction<String>(“Kotlin”)
}

We get this error on the second print statement.

Cannot use ‘T’ as reified type parameter. Use a class instead.

This is due to type erasure. This means that when someFunction<String> (“Kotlin”) is called in the main function, at compile time, the compiler removes the type argument <String> and this is what is left at runtime someFunction(“Kotlin”). Therefore, at runtime, we cannot possibly know exactly which type the T stands for.

Workaround (Java way)

The java workaround for this is to pass Class of T (Class (for Java) and KClass (for Kotlin)) in addition to specifying the type bound Any. This solution works but has a lot of boilerplate code. 

fun <T: Any> someFunction(value: T, type: KClass<T>) {
    println(“The value is: $value”) 
    println(“Type is: ${type.simpleName}")
}

fun main() {
    someFunction<String>(“Kotlin”, String::class.java)
}

With reified

The same can be achieved in Kotlin using an inline function with reified type parameter T.

inline fun <reified T> someFunction(value: T) {
    println(“The value is: $value”)
    println(“Type is: ${T::class.simpleName}")
}

How to use it?

  • Mark the type parameter as reified.
  • Make sure that the function is inline

Reified can only be used with inline functions because when the compiler copies the function body to its caller, it replaces the type parameter T with the actual type argument. So the main function becomes  like shown below.

fun main() {
    println(“Type is: ${String::class.simpleName}")
}

When to use it?

We can use reified for some of the following reasons

  1. Access type information of type parameter T at runtime. (as seen in previous example)
  2. Perform type checks

Without reified, we can’t directly perform type checks due to type erasure. We must explicitly pass the Class object as a parameter to check if the object is an instance of String. With reified, we can directly perform type checks without explicit Class parameter.

fun <T> isInstanceOf(obj: Any, type: KClass<T>): Boolean {
    return type.isInstance(obj)
}

fun main() {
    println(isInstanceOf("Kotlin", String::class.java))   // true
    println(isInstanceOf(21, String::class.java))         // false
}

   3.    Safely cast objects
Without reified, to safely cast an object to a specific type, we need to pass the Class object again.

fun <T> castToType(value: Any, type: Class<T>): T? {
    return if (type.isInstance(value)) {
        type.cast(value)
    } else {
        null
    }
}

fun main() {
    val result: String? = castToType("Kotlin", String::class.java)
    println(result) // Hello

    val resultInt: Int? = castToType("Kotlin", Int::class.java)
    println(resultInt) // null
}

With reified, we can safely cast an object within the function and the code becomes much simpler.

inline fun <reified T> castToType(value: Any): T? { 
    return value as? T 
}

Important things to note

  • Inline Function is required: To use reified, a function must be inline.
  • No Java interoperability: An inline function with a reified type parameter cannot be called from Java.
  • Performance: Inline functions should be used carefully as they can lead to larger bytecode size (since the function’s body is copied at each call site) and hence could impact performance.

Conclusion

The reified keyword in inline functions allows you to retain type information at runtime, making the operations straightforward and eliminating the need for passing Class objects.

Reified is used to

  • Access type information at runtime
  • Perform Type checks
  • Safely cast objects
  • Simplify code (Cleaner Syntax)

References

https://kotlinlang.org/docs/inline-functions.html#reified-type-parameters