Inline and Reified Type Parameters in Kotlin
Resolve lambda overhead and overcome type erasure
Inline functions replace the function call with the body of the function itself. Hence the overhead of the function call is saved. The overhead is much more when the function is a higher order function, i.e, contains a lambda as a function parameter. Inline functions come to the rescue and inline the called function as well as lambda body.
Reified Type Parameters along with inline keyword in Kotlin are used in the context of generics. At runtime, any information about the actual type arguments used by the generic class instances are erased, which means we cannot access the generic type <T> during runtime. Type Erasure exists in Java as well. However, Kotlin provides a way to access the type parameter during runtime using reified with inline.
inline fun execute() {
print("Hello")
}
The decompiled Kotlin byte code(Tools->Kotlin->Show Kotlin Bytecode->Decompile) for the execute() function call is somewhat as shown below:
int $i$f$execute = false;
String var4 = "Hello";
boolean var5 = false;
System.out.print(var4);
To elaborate, using inline keyword, the compiler replaces the call to the inline function with the body of the function.
The higher order functions in Kotlin are converted into class objects, implementing the lambda function, at compilation time and a virtual call to the function is made when the lambda is invoked. This is all over an expensive operation and introduces run time overhead due to memory allocations for class and object and the virtual function call.
fun execute(myfun : () -> Unit) {
myfun()
}
The decompiled Kotlin bytecode for the above function is :
public final void execute(@NotNull Function0 myfun) {
Intrinsics.checkParameterIsNotNull(myfun, "myfun");
myfun.invoke();
}
Here, we can see that the invoke() function is being called on the created myfun object. It works like shown below in Java :
execute(new Function0() {
@Override
public void invoke() {
print(“Hello”);
}
});
The overhead due to memory allocations would be larger if multiple lambdas are high.
We can overcome this runtime overhead by using the inline keyword as shown below:
inline fun execute(myfun : () -> Unit) {
myfun()
}
For the execute function call,
execute { print("Hello") }
the decompiled Kotlin bytecode for the execute() function call now becomes as shown below:
int $i$f$execute = false;
int var4 = false;
String var5 = "Hello";
boolean var6 = false;
System.out.print(var5);
Here, we can see that the call to the execute function is replaced by the code in the body of the execute function.
Reified Type Parameters
As said above, any information about the actual type arguments used by the generic class instances are erased and not available at runtime. Sometimes, we need to access the generic type parameter at runtime. This can be done by using inline keyword combined with reified modifier with the type parameter. To use reified type parameters, a function has to be an inline function.
See the example below for reference.
inline fun <reified T> execute(input: T) {
if(T::class == String::class) {
println((input as String).length)
} else if(T::class == Int::class){
println(input)
}
}