Explanation of inline, noinline, crossinline, and reified in Kotlin
inline, noinline, crossinline, and reified are so confusing Kotlin keywords. I will try to explain why should we use that Keyword with some examples. I hope this will help you to clear up the confusion. Let’s jump to the first one inline.
inline functions
The inline keyword is used to mark a function as an inline function. An inline function in Kotlin is a type of function in which instead of calling the function and executing the function body, the function body is copied and pasted into the function call site rather than creating a separate function object. This process is called inlining. The mostly inline keyword is used in higher-order functions or lambda functions in kotlin. However, the inline keyword in general functions impact is insignificant.
inline fun measureTimeMillis(block: () -> Unit): Long {
val startTime = System.currentTimeMillis()
block()
return System.currentTimeMillis() - startTime
}
In this example, the measureTimeMillis
the function takes a lambda expression as an argument and returns the time taken to execute the lambda expression. By marking the function as inline
, the function call overhead is eliminated by taking the lambda function body coping and paste into the call site and executing the code, which can lead to better performance when measuring the time taken to execute small code blocks.
fun main() {
val executionTime = measureTimeMillis {
// some code to be measured
}
println("Execution time: $executionime ms")
}
It’s important to note that we should use inline
for small functions that are called frequently otherwise, the bytecode bloat caused by inlining can lead to larger APK sizes and longer compilation times.
Why should use inline
functions
You should use inline
function to improve the performance of your code by eliminating overhead.
The inline
function is used to improve the performance of higher-order functions by avoiding the creation of unnecessary objects at runtime. when we declare a function as inline
, the compiler replaces the function call site with the function’s code at compile time, instead of creating an object at run-time.
Additionally, inline functions can also be used to improve the readability and maintainability of the code by reducing the number of anonymous classes and lambdas used in the code.
However, it’s important to note that the use of inline
functions should be limited, as they can increase the size of APK and may not be necessary for smaller and less complex functions.
noinline functions
In Kotlin, noinline keyword is used to indicate that a lambda function passed as an argument to another function should not be inlined. This means that these parameter lambda functions behave as regular functions. Here are some examples of using noinline
in Kotlin:
- Passing a lambda with a
return
statement to a higher-order function:
inline fun manageContacts(contacts: List<Int>, callback: (Int) -> Unit) {
for (contact in contacts) {
callback(contact)
}
}
fun main() {
val contacts = listOf(1, 2, 3, 4, 5)
// Using a lambda without return
manageContacts(contacts) { contact ->
println(contact)
}
// Using a lambda with return
manageContacts(contacts) {
if (it == 3) return
println(it)
}
}
In the above example, if the callback
lambda passed to manageContacts
contains a return
a statement, it will return from the entire function instead of just the lambda. To avoid this, we can use noinline
to prevent the lambda from being inlined and allow for the return
statement to only affect the lambda.
inline fun manageContacts(contacts: List<Int>, noinline callback: (Int) -> Unit) {
for (contact in contacts) {
callback(contact)
}
}
2. Using a lambda with receiver:
inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
class Person(var name: String, var age: Int)
fun main() {
val person = Person("John Doe", 30)
// Using apply with lambda without receiver
person.apply {
name = "Jane Doe"
age = 25
}
// Using apply with lambda with receiver
person.apply {
this.name = "John Smith"
this.age = 35
}
}
In the above example, apply
is an inline function that takes a lambda with a receiver. By default, lambdas with receivers are always inlined, but if we want to pass a lambda with a receiver as a parameter to another function, we can use noinline
to prevent it from being inlined.
inline fun <T> T.apply(noinline block: T.() -> Unit): T {
block()
return this
}
When to use noinline keyword
- When working with higher-order functions that take lambda parameters. If you have a higher-order function that takes multiple lambda parameters, and you only want to allow one of them to escape the function, you can mark the other parameters as
noinline
. - When you need to optimize performance. If you have a function that is called frequently and takes lambda parameters, mark those parameters as
noinline
can help reduce the amount of object creation and garbage collection, leading to improved performance.
crossinline functions
The crossinline
keyword is used to indicate that a lambda function passed as an argument to the higher-order function should not be able to access variables from the surrounding scope.
When a lambda function is passed as an argument to another higher-order function, it can access the variables from the surrounding scope. This is called capturing the variables.
For example,
fun bar(callback: () -> Unit) {
val x = 5
callback()
}
This higher-order function takes a lambda function as an argument, and inside the function, it defines a variable “x” and calls the lambda. If the lambda inside the function references the variable “x”, it is said to have captured that variable.
In some cases, you might not want the lambda function to capture any variables from the surrounding scope and you can use crossinline keyword to prevent it.
fun bar(crossinline callback: () -> Unit) {
val x= 5
callback()
}
Using the crossinline keyword can help to prevent unwanted behavior that can happen when a lambda captures variables from the surrounding scope. It also helps to make the code more readable and easier to understand, since it makes it clear that the lambda function can’t access the variables from the surrounding scope.
The crossinline keyword should be used when you want to ensure that a lambda function passed as an argument to another function cannot access variables from the surrounding scope. It is useful when you want to prevent unwanted behavior that can occur when a lambda captures variables from the surrounding scope. Additionally, using crossinline can improve the readability and clarity of your code by making it clear that the lambda cannot access surrounding variables. However, it is important to note that using crossinline can lead to unexpected behavior if the lambda is meant to use surrounding variables, so it should be used carefully and only when necessary.
reified in function
The reified keyword in Kotlin is used to specify that a function or class’s type parameter should be substituted with its concrete type at runtime. This allows the function or class to leverage the actual type information of the type parameter instead of treating it as a generic type.
For example, consider the following function:
inline fun <T> typeName(t: T) {
println(t::class.java.name)
}
This function takes a generic type T as an argument and prints the name of the class that passed as an argument. However, since T is a generic type, the function does not know its actual class at compile time.
To allow the function to use the actual type information of the type parameter at runtime, you can use the reified keyword as follows:
inline fun <reified T> typeName(t: T) {
println(t!!::class.java.name)
}
Another example is here:
inline fun <reified T> isType(value: Any): Boolean {
return value is T
}
This function can take any value and check if it is of a specific type. The type is specified as a type parameter T, and the function uses the reified keyword to enable it to check if the value is of the specified type T at runtime. Because of this, the function can access the actual type information of the type parameter T at runtime and check if the passed value is of the same type.
The reified keyword in Kotlin is used in the following scenarios:
- When there is a need to perform type checks or typecasts on a type parameter at runtime.
- When reflection needs to be used on a type parameter at runtime.
- When a class needs to be instantiated by its type parameter at runtime.
- When the actual type information of the type parameter needs to be used in a performance-critical section of the code.
Hope this would be helpful!
If you want, you can connect me on Linkedin!
If you like my content and want to support my work, you can buy me a cup of coffee ☕️ 🥰