Understanding the reified Keyword in Kotlin

In Kotlin, the reified
keyword is a powerful feature that allows us to retain type information at runtime, which is otherwise erased due to type erasure. This is particularly useful when working with generics and inline functions. In this blog post, we'll explore what the reified
keyword is, why it's useful, and how to use it with code examples.
What is Type Erasure?
Before diving into reified
, it's important to understand type erasure. In Java and Kotlin, generic type information is erased at runtime. This means that if you have a generic function like this:
fun <T> getSecondItem(list: List<T>): T {
return list[1]
}
At runtime, the type T
is erased, and the function essentially becomes:
fun getSecondItem(list: List<Any?>): Any? {
return list[1]
}
This is done for backward compatibility with older versions of Java that didn't support generics. However, this erasure can be problematic when you need to perform operations that depend on the type information at runtime.
The Problem with Type Erasure
Consider the following function that checks if the second item in a list is of a specific type:
fun <T> isSecondItemOfType(list: List<Any>, clazz: Class<T>): Boolean {
return clazz.isInstance(list[1])
}
This works, but it's a bit verbose because you need to pass the Class
object explicitly. Ideally, we'd like to avoid passing the Class
object and instead infer the type directly.
Enter reified
Type Parameters
The reified
keyword allows us to retain type information at runtime when used with inline functions. Let's rewrite the above function using reified
:
inline fun <reified T> isSecondItemOfType(list: List<Any>): Boolean {
return list[1] is T
}
Here’s what’s happening:
inline
: The function is marked asinline
, which means the function's code is copied directly into the call site at compile time.reified
: The type parameterT
is marked asreified
, which means the type information is preserved at runtime.
Now, you can call this function without passing the Class
object:
val list = listOf("Apple", "Banana", 42)
val isSecondItemString = isSecondItemOfType<String>(list) // false
val isSecondItemInt = isSecondItemOfType<Int>(list) // true
How Does It Work?
When you mark a type parameter as reified
, the Kotlin compiler ensures that the type information is available at runtime. This is achieved by inlining the function, which essentially "copies" the function's body into the call site. Since the type information is known at the call site, it can be used directly.
Decompiled Java Code
If we decompile the Kotlin bytecode into Java, the isSecondItemOfType
function would look something like this:
public static final boolean isSecondItemOfType(List list) {
return list.get(1) instanceof String; // or Int, depending on the call site
}
As you can see, the type check is performed directly at the call site, and the type information is preserved.
Use Case: Deserialization with reified
Another common use case for reified
is when deserializing data, such as JSON or XML, into objects. For example, consider a scenario where you're using JAXB to deserialize XML into a Kotlin object:
Here, T::class.java
retrieves the Class
object for the type T
, which is then used by JAXB to deserialize the XML. This allows you to write cleaner code at the call site:
val product: Product = unmarshaller.unmarshal("<product>...</product>")
Without reified
, you would need to pass the Class
object explicitly:
val product = unmarshaller.unmarshal("<product>...</product>", Product::class.java)
Trade-offs of Using reified
While reified
type parameters are powerful, there are some trade-offs to consider:
- Function Inlining: Since
reified
type parameters require the function to beinline
, the function's body is duplicated at each call site. This can lead to increased code size if the function is large or called frequently. - Java Interop: Functions with
reified
type parameters cannot be called from Java. If your project involves Java interop, this could be a limitation.
Conclusion
The reified
keyword in Kotlin is a powerful tool that allows you to retain type information at runtime, enabling you to write more expressive and concise code. It’s particularly useful when working with generics, type checks, and deserialization. However, it’s important to be mindful of the trade-offs, such as increased code size and limitations with Java interop.
By combining inline
functions with reified
type parameters, you can achieve things that would otherwise be impossible due to type erasure. So the next time you find yourself struggling with type information at runtime, consider reaching for the reified
keyword!
If you enjoyed this post and want to learn more about Kotlin, consider subscribing to my newsletter. You’ll be the first to know about new articles, videos, and projects I’m working on!
Thanks for reading, and happy coding! 🚀