Generics in Kotlin

A quick introduction to using generics and understanding erasure in Kotlin

Logeshvar L
FAUN — Developer Community 🐾

--

source: https://unsplash.com/photos/7tXA8xwe4W4

Generics are a great deal when it comes to writing reusable code — classes, interfaces, and functions all can use generics. Good design emphasizes writing reusable code and the abstraction of concepts, among others. By using generics we can easily achieve reusability and abstraction.

Kotlin is based on the JVM runtime and obviously, like Java, Kotlin also supports generics. It also provides additional functionality with generics than Java. Am I kidding? No.

Kotlin introduces the concepts of reified parameters, declaration-site variants and use-site variants based on generics. Let’s understand generics first.

Generics

I wish to write a simple function to print all the elements in a list of Int. It would look like this:

The same if I wish to do for a list of Shorts,

But we know the numbers in our code are not limited to just Int and Short. We have a boatload of Number types and we cannot go about writing separate functions that accept each Number type. We have to avoid repeated code. That’s the basic rule every programmer should know by heart and follow without fail.

Generics are a helping hand in such cases. Generics normalize the type of the class/function/interface and let it take any type matching the generic type mentioned. To use Generics in our code and to express it as generic we need to add the type parameter declaration in our function/class/interface. Let’s see how this works.

Here, we can see how a single function can work for all the List subtypes, without any hiccup. To make this more Kotlin-like, we can also add this as an extension function if this will be used widely across your project.

And the use of generic type parameter in the function parameter can also be used in the function return type, class declaration, interfaces etc.

Use of generics in interface List<out E>
Use of generics in List interface
Use of generics in function return fun get returning value of generic type in List interface
Use of generics in List<E>.get() function return

For functions that want to define boundaries, we can also specify the superclass it must extend as we do in Java. This can be done as follows in Kotlin.

Let’s say I want to create a function, that converts given input to Int type and returns it. I know it’s dumb, but to give the context this is a simple and easy to understand example.

I can write something like this. And I’m using generics so it can accept any type and I don’t have to repeat my code. An array can be passed, a map can be passed, a string can be passed, and a short can be passed according to the generic type here.

But, the compiler is already complaining to me that it cannot find such a function. But why? Because, not all of them have the toInt() function. Only a few do. The default upper bound for generic type when nothing is specified is Any?

Hence, we cannot expect all possible inputs to have the toInt() method. We have to restrict the range of the generic type, to accept only types that can be converted to Int.

Now by adding the supertype (Number) it must extend, we’re able to achieve our requirement smoothly. It can now only accept input parameters that only extend the Number class and not a String type or List or Map type.

Only one type can be specified inside the type param declaration. And if we want the generic type to extend multiple types at the same time we can use the “where” clause.

Now that we’ve seen generics’ use, we can understand the advantage it provides. It is highly powerful but yet still there are some cases where you can’t utilize the functionality completely.

Generics are restricted to compile-time, they’re not carried over to runtime. What do I mean by that? Generics can hold the type information only till compilation, once the code is compiled and starts executing it loses the information as it converts to byte code. This nature can be seen explicitly when we make type checks (like instanceOf in Java). This nature of generics is called Erasure.

Consider, we take a list input and we want to check if it is an Int List. For this, in Kotlin, we do an “is” type check. Let’s write a simple function.

The compiler is already complaining it cannot do that kind of check even before running. And it says it is an erased type. And suggests a different approach to tackle this.

Here, we’ve used the List<*> to do a type check, which means it must be a list of any type. And “*” is called the star-projection operator to match with any type of List. And inside the if block, we are casting it to a List<Int>.

But, the compiler already shows two warnings.
One, the if check is always true, as we take a list only as input.
And two, the cast inside the if check is unchecked and can fail, obviously. Hence, it is unsafe to do such checks.

To overcome this, Kotlin uses the concept of reified parameters that can be used in inline functions. More on this later.

Hope you found this article helpful and resourceful. Thanks for reading this! Please share your thoughts/comments on this!

Let’s make coding fun together!

Join FAUN: Website 💻|Podcast 🎙️|Twitter 🐦|Facebook 👥|Instagram 📷|Facebook Group 🗣️|Linkedin Group 💬| Slack 📱|Cloud Native News 📰|More.

If this post was helpful, please click the clap 👏 button below a few times to show your support for the author 👇

--

--