inline function — un mecanismo de Kotlin en el que el cuerpo de la función se sustituye directamente en cada lugar de llamada en tiempo de compilación. Esto elimina la sobrecarga de crear clases anónimas y objetos para los parámetros lambda. Según Kotlin Documentation, 2025, la palabra clave inline es especialmente eficaz para funciones de orden superior, donde cada lambda sin inserción crea un objeto FunctionN independiente, sobrecargando el recolector de basura.
Puntos clave
Una función inline es una función marcada con la palabra clave inline. El compilador de Kotlin no crea un bytecode separado con una llamada, sino que copia el cuerpo de la función directamente en cada lugar de llamada. El objetivo principal es optimizar funciones de orden superior que aceptan expresiones lambda, ya que cada lambda en una situación normal crea un objeto de clase Function anónimo.
Según JetBrains Tech Blog (2024), el uso de funciones inline en Kotlin puede reducir la cantidad de objetos creados en un 40–60% en funciones que utilizan lambdas intensivamente. En bucles y operaciones de alta carga (ordenación, filtrado de colecciones) esto proporciona una ganancia de rendimiento medible.
Sin inline, cada lambda se compila en una clase anónima (o una instancia de una interfaz funcional sintetizada). Para las lambdas que capturan variables, se crean objetos envoltorio adicionales. Las funciones inline eliminan todos estos objetos en tiempo de compilación, reemplazándolos con código directo que accede a variables locales sin envoltorios.
Use inline solo para funciones con parámetros lambda — el compilador de Kotlin advierte si inline no proporciona beneficios.
Simplemente agregue la palabra clave inline antes de la declaración de la función. El compilador sustituye automáticamente el cuerpo de la función en los lugares de llamada. La función en sí continúa existiendo en bytecode para los casos en que no se llama directamente (por ejemplo, desde código Java).
inline fun Int.repeatAction(action: (Int) -> Unit) {
for (i in 0 until this) {
action(i)
}
}
// Call — lambda code is inlined into the function body
5.repeatAction { index ->
println("Index: $index")
}
Después de la compilación, el código anterior será equivalente a:
// What happens after inlining (schematically):
val $this = 5
for (i in 0 until $this) {
println("Index: $i")
}
No se crean objetos para la lambda — el código action se ejecuta directamente. Esta es la esencia de la optimización: en lugar de llamar a Function.invoke() — una inserción directa del código con el cuerpo de la lambda.
Para verificar la inserción, abra Tools > Kotlin > Show Kotlin Bytecode en IntelliJ IDEA y haga clic en Decompile. Verá que en lugar de llamar a repeatAction con una lambda, se genera una inserción directa del cuerpo de la función con un bucle for.
Cada lambda en Kotlin se compila en una de tres variantes. Primera — si la lambda no captura variables, se convierte en un método estático de la clase donde se declara. Segunda — si captura una variable, se crea una clase anónima. Tercera — si captura varias variables, se crea una clase anónima con campos para cada variable capturada.
| Tipo de lambda | Sin inline | Con inline |
|---|---|---|
| Sin captura | Un método estático (reutilizado) | Inserción completa, sin llamada |
| Con captura de 1 variable | Clase anónima (un objeto) | Inserción completa, sin objeto |
| Con captura de N variables | Clase anónima con N campos | Inserción completa, sin objeto |
| Recursiva | Llamada normal | inline prohibido |
Según Android Performance Patterns (Google, 2024), en aplicaciones con uso intensivo de colecciones (filtrado, ordenación, agrupación) las funciones inline reducen las asignaciones en un 25–35%. El efecto es especialmente notable en Jetpack Compose, donde cada cambio de estado provoca una recomposición con muchas lambdas.
Una lambda en una función normal no puede hacer return desde la función externa — solo un return local desde la propia lambda (mediante return@label). En una función inline, la lambda se inserta en el cuerpo de la función llamante, por lo que nonlocal return se vuelve posible: un return dentro de la lambda termina la función externa.
inline fun findFirst(
items: List<Int>,
predicate: (Int) -> Boolean
): Int {
for (item in items) {
if (predicate(item)) {
return item
}
}
return -1
}
fun processNumbers() {
val numbers = listOf(1, 2, 3)
val firstEven = findFirst(numbers) { it % 2 == 0 }
// return in lambda would return null from processNumbers()
}
Nonlocal return es conveniente para la terminación anticipada, pero puede provocar errores. Si la lambda se usa en un contexto no local (almacenada en una variable), un nonlocal return causará una RuntimeException. El compilador de Kotlin emite una advertencia al intentar dicho almacenamiento.
Cuando una función tiene varios parámetros lambda, a veces es necesario insertar solo algunos de ellos. Para esto se usa noinline — evita la inserción de un parámetro lambda específico, dejándolo como un objeto Function normal.
El modificador crossinline resuelve el problema opuesto: la lambda se inserta, pero se prohíbe nonlocal return. Esto es necesario cuando la lambda se usa dentro de otra lambda o en un contexto donde return no está permitido (por ejemplo, pasado a un Runnable).
inline fun processWithCallback(
data: String,
crossinline onSuccess: (String) -> Unit,
noinline onError: (Exception) -> Unit
) {
try {
val result = process(data)
onSuccess(result)
} catch (e: Exception) {
onError(e)
}
}
// noinline: onError can be stored in a variable or passed elsewhere
val errorHandler = { e: Exception -> log(e.message) }
processWithCallback("input", { println(it) }, errorHandler)
En el ejemplo, onSuccess está marcada como crossinline — se insertará, pero no se puede usar return dentro de ella. onError está marcada como noinline — no se inserta, por lo que se puede pasar como objeto, almacenar en un campo de clase o usar como listener.
Las funciones inline tienen limitaciones. Las funciones inline recursivas están prohibidas — el compilador devolverá un error. Las funciones inline no pueden tener visibilidad private o internal si se declaran en otro módulo, pero esto es una limitación de visibilidad, no relacionada con el mecanismo de inserción en sí.
El tamaño del bytecode crece con cada llamada a función inline, ya que el cuerpo se copia. Según Kotlin Coding Conventions (JetBrains, 2025), se recomienda usar inline solo para funciones de hasta 10–15 líneas. Para funciones grandes, el beneficio de la inserción de lambdas puede verse anulado por el aumento del tamaño del APK (crítico en Android debido al límite de 64K métodos).
// Recommended practice
inline fun withLock(lock: Lock, action: () -> T): T {
lock.lock()
try {
return action()
} finally {
lock.unlock()
}
}
// Not recommended for large functions
inline fun largeComputation(...) { // bad — body >50 lines
// more than 50 lines — better to extract into a regular function
}
Las funciones inline públicas en bibliotecas requieren precaución: si el cuerpo de una función inline cambia, todos los clientes deben recompilarse. JetBrains recomienda usar @PublishedApi internal para los miembros llamados desde funciones inline para mantener la compatibilidad dentro de un módulo.
Preguntas frecuentes
Sí, inline extension function funciona sin restricciones. Por ejemplo: inline fun String.transform(block: (Char) -> Char): String. La extensión no afecta la capacidad de inserción — el compilador la maneja igual que una función inline normal.
Si la función no acepta parámetros lambda — inline no proporciona ventaja. El compilador de Kotlin emite una advertencia: “Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types.” Además, inline es perjudicial para funciones grandes debido al crecimiento del bytecode.
inline — un modificador de función que inserta el cuerpo de la función en el lugar de llamada. @JvmInline (value class) — un mecanismo para clases envoltorio que se reemplazan por su valor en tiempo de compilación. Conceptos diferentes: inline optimiza llamadas, value class optimiza la representación de datos.
No, las funciones suspend no pueden ser inline porque se compilan en una máquina de estados con Continuation. Sin embargo, una función inline puede aceptar una lambda suspend como parámetro con crossinline. Esto se usa a menudo en corrutinas: inline fun launch(block: suspend CoroutineScope.() -> Unit).
Sí, las funciones inline complican la depuración porque el cuerpo de la función no se llama sino que se inserta en el lugar de llamada. Los stacktraces se vuelven más largos, los puntos de interrupción funcionan pero pueden mostrar posiciones inesperadas. JetBrains recomienda depurar sin inline y habilitarlo solo en compilaciones release.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también