How it works
The explanation, with examples you can run.
What a lambda is
A non-inline lambda compiles to an object implementing a function interface (Function1<Int, String>, and so on; on newer JVM targets through invokedynamic). If it captures local variables, the object holds them; capturing a var wraps it in a small mutable box so both sides see changes. Every call through the function type is an interface call, and every capturing lambda that is created is an allocation.
fun counter(): () -> Int { var n = 0 return { ++n } // captures n in a box; lives on after counter() returns} fun main() { val next = counter() next(); next() println(next()) // 3}inline: no lambda at all
Marking a higher-order function inline tells the compiler to copy its body, and the bodies of lambdas passed to it, into every call site. No function object is created. The standard library's map, filter, forEach, let and use are all inline, so a list.filter { it > 0 } costs a loop, not a lambda allocation.
Inlining changes what return means:
fun firstNegative(xs: List<Int>): Int? { xs.forEach { if (it < 0) return it // non-local: leaves firstNegative } return null} fun printPositives(xs: List<Int>) { xs.forEach { if (it < 0) return@forEach // local: like continue println(it) }} fun main() { println(firstNegative(listOf(3, -1, -5))) // -1 printPositives(listOf(1, -2, 3)) // 1, then 3}Two modifiers adjust this per parameter: noinline keeps a lambda as a real object (so you can store it), and crossinline forbids non-local returns in a lambda that the inline function calls from another context, such as a Runnable it creates.
reified: generics that survive
On the JVM, type arguments are erased: inside fun <T> f() there is no T at runtime. An inline function is copied to each caller, where the type is concrete, so it can declare reified T:
inline fun <reified T> List<Any>.only(): List<T> = filter { it is T }.map { it as T } inline fun <reified T : Any> nameOf(): String = T::class.simpleName ?: "?" fun main() { val mixed: List<Any> = listOf(1, "a", 2, "b") println(mixed.only<String>()) // [a, b] println(nameOf<StringBuilder>()) // StringBuilder}filterIsInstance<T>(), Gson-style fromJson<T>() helpers and Koin's get<T>() all use this. Reified functions can't be called from Java, because Java has no way to inline them.
Use inline for small functions that take lambdas or need reified types. On anything else it only makes the bytecode bigger, and the compiler warns that the expected performance gain is insignificant.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.