Kotlin's reified and why you should use ...

Kotlin's reified and why you should use it(or maybe not)

Mar 15, 2024

Kotlin brings with it a lot of new features and a lot of sugar coated syntax which leads some people to say "that is so cool!! gotta catch them all!", others saying "oh great, now we can have even longer discussions about the best way to do this", and others saying "what, can't you do this the old way?". One of those sugar coated or maybe not so sugar coated for some is the reified attribute used when creating and only when (at the time of this article), writing higher order functions. This bite is about what I have found so far about reified in Kotlin. What can and what it cannot do for us and of course this is also a reflection on how to go about with reified.


In Java, there is something you virtually cannot escape from when you are making reusable and modular software. I'm talking about Generics, and if you know anything about Generics, you already came across the letter T which is the almighty default letter for Generic Types in the Java world. When we use generics, we are, during design time, essentially creating different classes. This means that a class like class Person<Bag> is not the same class as class Person<Suitcase> even though they come from the same generic class Person<T>. This is the main reason why type erasure has been designed. And this has worked for years. Long story short, type erasure strives to reduce the overhead of generics by removing the type from these extra cases and compiling the code in such a way that you'll only have Person as a class in the byte code. This way, the compiler greatly reduces/eliminates the overhead introduced by using generics.


Developers in the Java world, usually "don't even need to know this", except when they struggle with "type cast" warnings in their favourite IDE which tells them that something is wrong. Sometimes its just a warning and it can lead to serious type casting runtime errors in the code that were not so visible in the warning during design time. Type cast issues during design time can become very convoluted, if you don't know how type erasure works, which honestly speaking is more like a crum sized piece of information that every Java/JVM developer in all ranks should know.


And now we come to my example, which is a pretty simple one. As I kept developing my project, buy-odd-yucca-concert, I wanted at some point to use an ObjectInputStream, in order to read an object back from a ByteBuffer in a Redis cache. Reading it back lead m to the following extension function:

fun <T> ObjectInputStream.readTypedObject() = readObject() as T

What I was trying to achieve here is a helper extension function to return back a read object from an input stream. As you know ObjectInputStream does not have a generic method to return back a typed object. It does have readObject, but that is just an Object in Java which gets translated to Any in kotlin. None of them are useful in this case. Anyways, what's important in this exercise is the warning we get when we cast readObject() to a T : "Unchecked cast: Any! to T". "Bly me!!" right? Well, because this T is like any other T (which doesn't have to be T. it can be anything you want. TATO is also valid), it is a generic type and so it gets erased after compiling. This means that the runtime, has no idea what this T is. It will, however cast it to Object and that would work in this case. However, we need to avoid these kind of things, because they are error prone and when errors coming from this do happen, they can be extremely difficult to detect. In Java, you'd probably have to create some type checks like "if(variable instanceof Personen) then { ... }". In Kotlin, however, you can seriously shortcut this using reified.


From the actual english dictionary definition reify means: "make (something abstract) more concrete or real.". This is very much appropriate for the chosen keyword used in kotlin. And in this way our function changes to:

inline fun <reified T : Any> ObjectInputStream.readTypedObject(): T = readObject() as T

T is now reified. What this means is that this T will keep its type information at runtime. Because of this we can cast Any to T. In my opinion though, Intellij should give a warning even in this case. But hey, we are explicitly casting to a type that the runtime understands and so there is room for discussion I guess. Sounds awesome right? Well, not so fast. If you did not notice yet, check the start of the declaration. There is this new inline keyword at the beginning of the function declaration. Ok, now we jump into the negative side of reified. Inlining means that the compiler will not generate an object bytecode to use this function in memory. Instead it will make a bytecode copy of its body to all entry points of your application. Your generated byte code will increase in size because of this. For every copy the compiler makes, the type is thus preserved.. This way, the CPU will not allocate memory for this function. This is the only way a reified type can, at this moment, avoid the type erasure of the compiler in higher order functions. . And remember that there are no reified types (at least not like this) in Java and so it is highly unlikely that you can reuse this function in Java code anytime soon. This is probably not the best argument though because you can always say "Well, quit Java then!". Well, Java doesn't have this, probably because, since it is very unclear what benefits there are of using this, other than that it is "pretty" and not "vanilla" and that "in some cases it performs better", then there is also probably no point in adding it to the language any way. On the other hand Java does have some sort of reification in the array usage. Arrays are not type erased in Java and so in those cases, you may be better off using an Array. I will however keep using reified in my project, for experimental purposes only, but I do expect Kotlin to kick the need for inline functions in these cases. Only then, will this reified be more than just sugar syntax. But that is probably too much of a stretch at this point, or maybe not. That's up to JetBrains to figure out.


Reification, the way Kotlin does it, looks amazing and can be a benefit if you want to improve code readability. It just seems to raise questions though, because inlining is mandatory in these cases. In my project though I'm using a reified function that will be widely used in multiple concurrent requests. My application does have a performance concern but reification isn't the subject of the project. My function is also very small and the step to use it looks quite insignificant. Being that the case, then maybe reification, the way Kotlin does it, isn't so bad and could potentially and allegedly improve performance. It may also penalise performance, but how much? Is it significant? Does it matter to my project?. One thing is clear and you must consider this. The bigger the inline function you create, the bigger the performance impact will be. If your function is big enough, then the performance impact goes from an If to an absolute certainty. The more concurrent processes you use will also precipitate this problem further. You have to make choices and reification can be serious problem if you overlook the consequences/benefits. The last thing you want is at some point start questioning why your application went so slow at some point and you already forgot about all the sugar syntax you used in the code with reified. And also remember that reified per se is never the issue here. The fact that it makes it mandatory to use "inlining" is.

Enjoy this post?

Buy João Esperancinha a coffee

More from João Esperancinha