Inline Functions in Kotlin: When They Are Really Needed and When They Harm

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Inline Functions in Kotlin: Optimization Magic or a Trap for Beginners?



If you've ever written in Kotlin and looked into the standard library, you've probably seen the mysterious word inline before function declarations. You might have even added it to your code, following the example of "experienced" colleagues. But do you really understand when an inline function brings benefits, and when it only bloats the bytecode and slows down compilation?



This question is one of the most popular on Stack Overflow (thousands of views!), and for good reason. We won't copy dry manuals. Instead, we'll analyze three real scenarios where inline functions become your best friend, and one where it's better to avoid them. Let's go!



What is an Inline Function in 30 Seconds (Refresher)



When you mark a function with the keyword inline, the compiler does not create a separate object for it in memory. Instead, it literally "pastes" the function body into every place where it is called. Imagine copying a piece of code and inserting it directly at the call site — that's what happens under the hood.



Without inline:

fun measureTime(action: () -> Unit) {    val start = System.currentTimeMillis()    action()    println("Time: ${System.currentTimeMillis() - start}ms")}

fun main() { measureTime { /* your code */ }}


With inline:

inline fun measureTime(action: () -> Unit) {    val start = System.currentTimeMillis()    action()    println("Time: ${System.currentTimeMillis() - start}ms")}

fun main() { measureTime { /* your code */ }}


The difference is invisible to the eye, but the compiler will create completely different bytecode. In the first case, an anonymous lambda object will be created; in the second, the code will simply be copied. Now let's figure out when this is really important.



Scenario #1: Higher-order Functions with Lambdas (Most Common Case)



If your function takes another function as a parameter (a so-called higher-order function), each lambda call creates an anonymous class. In a hot code path called thousands of times per second, this can lead to:

  • Unnecessary heap object allocation
  • Increased garbage collector load
  • Performance loss of up to 10-20%


When inline saves the day:

  • You frequently call the same function with lambdas in a loop
  • Your function is part of a library, and you don't know how it will be used
  • The lambda captures many variables (inline reduces overhead)


// Bad without inline (if called frequently)fun repeatAction(times: Int, action: () -> Unit) {    for (i in 0 until times) {        action()    }}

// Good with inlineinline fun repeatAction(times: Int, action: () -> Unit) { for (i in 0 until times) { action() }}


But there's a nuance: if your function is called rarely (once a minute) or the lambda is large, the benefit of inline approaches zero. Don't make functions inline "just in case."



Scenario #2: Reified Generics — The Only Way to Work with Types at Runtime



In Java and Kotlin, generics are erased during compilation. You cannot write if (value is T) or T::class.java inside a regular function. But with inline and reified, this becomes possible!



Real-life example — creating an instance by type:



// This will NOT compilefun <T> createInstance(): T {    return T::class.java.newInstance() // Error!}

// This works!inline fun <reified T> createInstance(): T { return T::class.java.newInstance()}


Without inline, you cannot use reified. This is the only case where inline is mandatory, not just

Blogs

Book Recommendations