7

How Kotlin’s “reified” Keyword Simplifies Working With Generics

 1 year ago
source link: https://betterprogramming.pub/how-kotlins-reified-keyword-simplifies-working-with-generics-50a4e0cb254c
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

How Kotlin’s “reified” Keyword Simplifies Working With Generics

How to access generic types in a Kotlin function body

0*VqxAbL7b7CLWT3gm

Photo by Birmingham Museums Trust on Unsplash

The Kotlin programming language comes with a great set of features that make a programmer’s life easier. Compared to Java, Kotlin offers many syntactic improvements and gimmicks that make programming a big pleasure. The reified keyword is one of them. This keyword is unknown in many other languages and therefore many people don’t directly understand its purpose. In this little article, I will be going through an example to demonstrate the keyword’s purpose and use.

What are we talking about, anyways?

Software needs abstraction in order to solve many different things at the same time. Many popular and powerful languages such as Java or C++ (even Golang, as I hear) have support for generic types. The following function is an example, written in Kotlin:

fun <T> myGenericFun(c: Class<T>)

In an ordinary generic function like myGenericFun, it is not possible to access the type T in the function body directly. The way we make it work is by passing an argument of type Class using the generic type T into the function. The reason why this is needed is that, like in Java, all generic types are unavailable at runtime. Generic types are being erased during compilation. For this reason, it is not quite simple to reference the generic type in the function body. For instance, in some cases, you will want to access the type of T within the implementation of your function. To do so, we need to explicitly pass the class as a parameter as done in the given example. This approach is totally correct and can be found in pretty much all Java code bases. However, Kotlin tries to simplify working with generics, which is where the reified keyword comes in.

The reified type to the rescue

If we take the example function from above and want to make use of the reified type Kotlin offers, the first prerequisite to follow is making the function inline. Inlined functions are resolved by the compiler and its code is being copied over to all call sites. If we now add the reified keyword to the generic type T our signature looks as follows:

inline fun <reified T> myGenericFun()

With such a function, we can now work with T as if it was a normal Class, e.g. you might want to check whether a variable is an instance of T, which you can easily do like this: myVar is T.

Under the hood

Remember that reified types can only be used in combination with inline functions. An inline function makes the compiler copy the function's bytecode to every place the function is called from. When you call an inline function with reified type, the compiler knows the actual type used as a type argument and modifies the generated bytecode to use the corresponding class directly. Therefore calls like myVar is T become e.g. myVar is String in the bytecode and can therefore work at runtime.

Reified in Action

Let’s have a look at an example to demonstrate what value reified actually brings. The task is to create an extension function on String that is supposed to convert a JSON string into a Kotlin type that gets specified by the function's generic type T. We will use com.fasterxml.jackson.module.kotlin for this and our first attempt looks as follows:

The first approach without reified type

fun <T> String.toKotlinObject(): T {
val mapper = jacksonObjectMapper()
//does not compile!
return mapper.readValue(this, T::class.java)
}

The readValue method used in the last statement takes a type as it’s last argument (here: T::class.java). This argument is used to specify the type the parsing is supposed to spit out. If we now refer to the type parameter T within the function body, the compiler complains with the following error message: "Cannot use 'T' as reified type parameter. Use a class instead.". This error basically tells us that the generic type T is not usable the way our function is set up. Let’s fix it in a first iteration.

Workaround with explicit Class parameter

fun <T: Any> String.toKotlinObject(c: KClass<T>): T {
val mapper = jacksonObjectMapper()
return mapper.readValue(this, c.java)
}

To work around the issue, we pass the Class of T explicitly, which we can simply pass through to readValue. This approach is super common in the Java world and works as intended. On the call side, our code looks like this:

// type definitiondata class MyJsonType(val name: String)
// variable definitionval jsonTypeAsString = """{"name":"example"}"""
// call to String::toKotlinObjectjsonTypeAsString.toKotlinObject(MyJsonType::class)

The Kotlin way using reified

Using an inline function with reified type parameter T makes it possible to implement our function as follows:

inline fun <reified T: Any> String.toKotlinObject(): T {
val mapper = jacksonObjectMapper()
return mapper.readValue(this, T::class.java)
}

There’s no need to pass the Class of T additionally, T can be used as if it was an ordinary class. For the client, the code reduces to the following:

json.toKotlinObject<MyJsonType>()

Interoperability with Java

Inline reified functions are not callable from Java code, whereas normal inline functions are. This might be one of the reasons why not all generic types that are used in inline functions become reified by default.

Conclusion

We have seen that the reified keyword can solve a common issue of the JVM’s generic type erasure. Many situations need us to access the generic type within the function body which is cumbersome to achieve on the function and also the callee side. This article went through a simple example to demonstrate the power of a reified type simplifying both the function implementation and the caller code.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK