Kotlin Inline Function Samples

İbrahim Ethem Şen
6 min readApr 20, 2023

--

I will try to explain the usage of the keywords Inline, Reified, Noinline, and Crossinline in functions in the Kotlin programming language.

When programming, we can generally evaluate what we write under two main categories without considering external operations:

Compile Time: It is the process of converting the code in the programming language into code that the compiler can understand and execute.

Run Time: It is the time when the program is executed. We can think of this as the moment we use an Android application.

When our code is executed, it follows a sequence and various operations are performed while taking the keywords into account. At a point where there is a function, it goes to the location of the function and performs the operation and continues from where it left off.

After completing the operation, we understand that it keeps the operations in the Back Stack from the same location it left off. In Kotlin, the Inline keyword changes this situation.

Inline: It buries the body of the function in Compile Time where it is called. Let’s examine it through examples.

override fun onCreate(savedInstanceState: Bundle?) {
...
repeatInlineFunction(2){
println("Android Kotlin")
}
}

inline fun repeatInlineFunction(times: Int, action: () -> Unit) {
for (index in 0 until times) {
action()
}
}

Since we used the Inline keyword together with the repeatInlineFunction, the body of the function is buried where it is called during compilation, so it will actually appear as follows:

override fun onCreate(savedInstanceState: Bundle?) {
...
for (index in 0 until 2) {
println("Android Kotlin")
}
}

To better understand, let’s use Show Kotlin Bytecode in Android Studio. Here, we can see how the bytecode version of Kotlin code appears in Java. Let’s remember that Kotlin code is not provided as Java code one-to-one.

If you notice, there is no called function here. Operations are directly performed with For loop. The value 2 given in the parameter is also visible.

What would be the advantage of using Inline functions in this way?

Reified: Enables our functions to be used as Generics.

Inline functions are fast: By burying the body where the function is called instead of a series of operations in the background, Track-Back stack, Inline functions are made faster.

Return: Inline functions can use Return instead of calling the function, as it buries the body in the code.

Reified: The bodies of Inline functions are buried where they are called at Compile Time. However, Generic Types are not known at Compile Time. Let’s examine an example.

inline fun <T> reifiedFunction() {
//Error Cannot use 'T' as reified type parameter. Use a class instead.
print(T::class.simpleName)
}

When we use Reified, it determines the Type that is expected to go to the location where the code is executed at Compile Time and places it there.

override fun onCreate(savedInstanceState: Bundle?) {
...
reifiedFunction<Int>()
reifiedFunction<String>()
}
inline fun <reified T> reifiedFunction() {
print(T::class.simpleName)
}

Let’s take a look at the Java code from the Kotlin Byte Code.

When we look at the Java code, we can see that it is created with the String-Int types that we have specified in the code where we call the function.

An example of using Inline-Reified in projects is for the Gson extension in CI/CD — Remote Config.

inline fun <reified T> FirebaseRemoteConfig.fetchToLiveData(
key: String,
gson: gson
): LiveData<T> {
val liveData = MutableLiveData<T>()
this.fetchAndActivate().addOnCompleteListener {
if (it.isSuccessful) {
val keyString = this.getString(key)
val type = object : TypeToken<T>() {}.type
val jsonModel = gson.fromJson<T>(keyString, type)
liveData.postValue(jsonModel)
}
}
return liveData
}

Return :

override fun onCreate(savedInstanceState: Bundle?) {
...
returnFunction()
}
private fun returnFunction() : Int{
returnNoinline(2){
if (it == 1){
return //'return' is not allowed here
}
}
returnInline(2){
if (it == 1){
return 3
}
}
return 0
}
fun returnNoinline(times: Int, action: (Int) -> Unit) {
for (index in 0 until times) {
action(index)
}
}
inline fun returnInline(times: Int, action: (Int) -> Unit) {
for (index in 0 until times) {
action(index)
}
}

While we can return a value within our Inline function, we cannot do such operation for a regular function. We achieve this functionality thanks to the inlining of the body.

NoInline-CrossInline

  • Noinline : We use it when we do not want the operation to be Inline.
  • Crossinline: We use it when we want the function to be Inline but want the operation to be performed in a different scope.

Let’s take a look at an example. Let’s write an extension for a EditText for the Focus-Unfocused state.

inline fun EditText.customizedOnFocusChangeListener(
functionFocus:() -> Unit,
functionUnfocused: () -> Unit
){
functionFocus() // OK
functionUnfocused() // OK
}

When we call it within the EditText scope, we don’t have any issues with High-Order functions. Now let’s add an OnFocusChangeListener.

inline fun EditText.customizedOnFocusChangeListener(
functionFocus:() -> Unit,
functionUnfocused: () -> Unit
){
this.onFocusChangeListener = View.OnFocusChangeListener { _, focus ->
if (focus){
//Error Can't inline 'functionFocus' here: it may contain non-local returns.
functionFocus()
}else{
//Error Can't inline 'functionUnfocused' here: it may contain non-local returns.
functionUnfocused()
}
}
}

We are now calling the High-Order function in a different scope. This is where we apply the CrossInline and NoInline usage.

override fun onCreate(savedInstanceState: Bundle?) {
edittext.customizedOnFocusChangeListener({
println("crossInline")
}){
println("noInline")
}
}

inline fun EditText.customizedOnFocusChangeListener(
crossinline functionFocus:() -> Unit,
noinline functionUnfocused: () -> Unit
){
this.onFocusChangeListener = View.OnFocusChangeListener { _, focus ->
if (focus){
functionFocus()
}else{
functionUnfocused()
}
}
}

Now, we can use our codes Inline but within different scopes or prevent them from being inline. While the functionFocus parameter is Inline but will be in a different scope, the functionUnfocused parameter will never be inline. To better understand this, let’s also take a look at the Java code.

In CrossInline, the Body is embedded into the code, while in NoInline, the function is called.

We briefly looked at the advantages of Inline functions. Now let’s take a look at where we shouldn’t use them.

Since the body of Inline functions is embedded into the code, it can cause the code to quickly grow. Let’s go back to our initial example and use multiple Inline functions.

override fun onCreate(savedInstanceState: Bundle?) {
...
repeatInlineFunction(2){
println("Android Kotlin")
}
repeatInlineFunction(2){
println("Android Kotlin")
}
repeatInlineFunction(2){
println("Android Kotlin")
}
repeatInlineFunction(2){
println("Android Kotlin")
}
}

inline fun repeatInlineFunction(times: Int, action: () -> Unit) {
for (index in 0 until times) {
action()
}
}

When we look at the code at Compile Time, it actually looks like this.

override fun onCreate(savedInstanceState: Bundle?) {
...
for (index in 0 until 2) {
println("Android Kotlin")
}
for (index in 0 until 2) {
println("Android Kotlin")
}
for (index in 0 until 2) {
println("Android Kotlin")
}
for (index in 0 until 2) {
println("Android Kotlin")
}
}

If we consider a case where Inline functions are called within each other, the code will quickly grow. Therefore, we should be careful with nested usage in places where we use Inline functions to prevent this situation.

  • We generally use Inline functions for small operations that are used frequently, such as the for loop used in text, which corresponds to the Kotlin repeat function.
  • We use Inline functions when we need to use reified types.

As we can observe, operations such as filter, map, etc. in Collections and scope functions such as apply, with, run, etc. are defined as Inline functions.

If you have any feedback or suggestions, feel free to reach out to me on LinkedIn or Twitter.

The original version of the text ->

https://medium.com/@ibrahimethemsen/kotlin-inline-function-examples-2cfca8d39dc1

--

--