Reified = Retained Type for Inline Functions

For smaller functions, the workaround is to make the function itself be inline while also adding the reified keyword to the generic type:

inline fun <reified T> Iterable<*>.filterByType() = this.filter { it is T }

fun main() {
  val raw = listOf(1, "this", 3, "is", "a", 1337, "mess")
  val filtered = raw.filterByType<String>()
  
  println(filtered)
}

This tells the compiler to do two things:

  1. As with all inline functions, the actual implementation should be placed into each call site, so while it looks like we are calling a function, in reality we are directly executing a copy of the function body
  2. In that copy of the function body, convert T into the actual type used by that particular call

In this case, we know that we want T to be String, because that is how we are calling filterByType(), using filterByType[String](). reified says that we take the T in the inline function and replace it by the actual type. So, it is as if we had written the code as:

fun main() {
  val raw = listOf(1, "this", 3, "is", "a", 1337, "mess")
  val filtered = raw.filter { it is String }
  
  println(filtered)
}

The filterByType() implementation is “inlined” where we make the call, and the concrete type for T is also inlined.

This is one of those keywords that you will tend to only use when the compiler yells at you, or maybe you encounter some runtime error. Any time you get an error related to type erasure, that suggests that you should be using inline and reified.

inline is really designed for small functions, where the overhead of having a bunch of copies of the function body is not too bad. If you get type erasure errors on a larger function, you will want to try to isolate the bit that has the actual type erasure problem, refactor that into a separate function, then inline that function and use reified to clear up the type erasure issue.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.