Las scope functions en Kotlin son cinco funciones de la biblioteca estándar (let, run, with, apply, also) que ejecutan un bloque de código en el contexto de un objeto. Se diferencian en la forma de acceder al objeto (mediante it o this) y el valor de retorno (objeto de contexto o resultado de la lambda). Según la documentación de Kotlin (2026), la elección correcta de la scope function reduce el código repetitivo en un 25–40%. Las scope functions ayudan a escribir inicialización, configuración y transformaciones de objetos en un estilo declarativo.
Puntos clave
Las scope functions son un conjunto de cinco funciones (let, run, with, apply, also) integradas en la biblioteca estándar de Kotlin. Cada una de ellas toma una lambda y cambia temporalmente el ámbito para que dentro de la lambda el código se ejecute en el contexto de un objeto específico. Esto evita repetir el nombre del objeto en múltiples llamadas y agrupa operaciones relacionadas en un solo bloque.
Dos características clave diferencian las scope functions: la forma de acceder al objeto de contexto (mediante this o mediante it) y el valor de retorno (el propio objeto de contexto o el resultado de la lambda). El acceso mediante this convierte al objeto en receptor implícito — los métodos y propiedades se llaman sin prefijo. El acceso mediante it pasa el objeto como parámetro explícito de la lambda. Devolver el objeto permite encadenar, devolver el resultado permite asignar el valor transformado.
Históricamente, las scope functions surgieron como un reemplazo del patrón clásico Builder y las variables temporales para inicialización. En lugar de crear una clase builder separada, el desarrollador escribe apply { propiedad1 = ...; propiedad2 = ... }. Esto reduce la cantidad de código y hace que la inicialización sea más legible. En el ecosistema de Kotlin, las scope functions se utilizan en todos los frameworks y bibliotecas como un modismo estándar del lenguaje.
La elección de la scope function correcta se determina por dos preguntas: si desea usar this o it para acceder al objeto, y si desea devolver el objeto mismo o el resultado de la lambda. La combinación de dos características binarias da cuatro opciones posibles, y with ocupa un lugar especial como la única función que no es una extensión.
| Función | Acceso | Retorno | ¿Extensión? | Escenario típico |
|---|---|---|---|---|
| let | it | resultado de lambda | sí | Transformación, verificación null |
| run | this | resultado de lambda | sí | Cálculo con contexto |
| with | this | resultado de lambda | no | Agrupación de llamadas |
| apply | this | objeto de contexto | sí | Inicialización de propiedades |
| also | it | objeto de contexto | sí | Efectos secundarios, registro |
Para configurar un objeto sin devolver un nuevo valor, use apply (acceso mediante this) o also (acceso mediante it). Para calcular un nuevo valor basado en un objeto, use let (acceso mediante it) o run (acceso mediante this). Para agrupar llamadas sin encadenamiento, use with. Seguir este esquema hace que el código sea predecible para otros desarrolladores familiarizados con Kotlin.
apply es una scope function que devuelve el objeto de contexto y proporciona acceso a él mediante this. Es la herramienta ideal para inicializar propiedades de un objeto después de su creación. Dentro del bloque apply se pueden establecer propiedades sin repetir el nombre del objeto. El método devuelve el propio objeto, lo que permite integrar apply en cadenas o usarlo en inicializadores.
data class ServerConfig(
var host: String = "localhost",
var port: Int = 8080,
var useTls: Boolean = false
)
val config = ServerConfig().apply {
host = "api.example.com"
port = 443
useTls = true
}
En el listado apply configura ServerConfig accediendo a las propiedades directamente mediante this (this se omite). Sin apply habría que escribir config.host = ..., config.port = ... — tres referencias repetitivas a la misma variable. La devolución del objeto permite guardar el resultado de la configuración en val config. also, a diferencia de apply, pasa el objeto mediante it y es adecuado para efectos secundarios.
fun saveUser(user: User) {
validate(user).also { result ->
println("Resultado de validación: $result")
}
val savedUser = user.also {
log("Saving user ${it.id}")
database.save(it)
}
}
En el ejemplo also se usa para registro — al devolver el objeto original, realiza un efecto secundario (mostrar un mensaje) sin modificar el objeto. Esto es una cadena: validate(user) devuelve User, also registra el resultado y pasa el usuario adelante. also también es útil para agregar elementos a colecciones o depurar en medio de una cadena de transformaciones.
let es una scope function que devuelve el resultado de la lambda y pasa el contexto mediante it. Es la función principal para transformaciones seguras con null: si el objeto es nullable, la combinación ?.let { } ejecutará el bloque solo para valores no nulos. let también se usa para limitar el ámbito de variables temporales y para transformaciones tipo map de un tipo a otro.
fun findUser(id: Int): User?
val displayName = findUser(42)?.let { user ->
"${user.name} (${user.email})"
} ?: "Unknown user"
// let con transformación de colección
val numbers = listOf("1", "2", "3")
val parsed = numbers.firstOrNull()?.let {
it.toIntOrNull()
} ?: 0
En el primer ejemplo let se llama mediante ?. — el bloque se ejecuta solo si findUser devolvió un valor no nulo. La lambda toma user mediante it (se puede renombrar para mayor claridad) y devuelve una cadena formateada. Si no se encuentra el usuario, el operador Elvis ?: proporciona un valor por defecto. En el segundo ejemplo let convierte una cadena en un número con un posible null.
run es un análogo de let con acceso mediante this, que devuelve el resultado de la lambda. run es conveniente para cálculos que requieren contexto del objeto pero devuelven un nuevo valor. Llamar a run() sin objeto (como run { ... }) crea un ámbito temporal para variables. with es una no-extensión con acceso mediante this, adecuada para agrupar llamadas sin encadenamiento.
// run como cálculo basado en contexto
val transformed = listOf(1, 2, 3).run {
filter { it > 1 }.map { it * 10 }
}
println(transformed) // [20, 30]
// with para agrupar llamadas
val info = with(StringBuilder()) {
append("Name: ")
append("Alice")
toString()
}
En el ejemplo run sobre una lista devuelve el resultado de filtrar y mapear — la transformación misma, no la lista. with toma StringBuilder como argumento (no mediante punto) y dentro trabaja con this: append, append, toString. El resultado de with es la cadena info. La elección entre run y with a menudo es subjetiva, pero with es preferible cuando el objeto se pasa desde otro lugar sin encadenamiento.
with se diferencia de otras scope functions en que no es una función de extensión. Toma el objeto como primer argumento y la lambda como segundo. Dentro de la lambda el objeto es accesible mediante this. with es conveniente para agrupar llamadas a métodos del objeto sin guardar el resultado o cuando se necesita “entre” temporalmente en un contexto para leer varias propiedades.
val user = User("Alice", "alice@example.com", "Admin")
val summary = with(user) {
"User $name has role $role and email $email"
}
println(summary)
// with para inicialización de componentes UI
with(TextView(context)) {
text = "Hello"
textSize = 18.0f
setTextColor(Color.BLUE)
}
En el primer ejemplo with forma la cadena summary accediendo a las propiedades de user directamente mediante this. Sin with habría que escribir "User ${user.name} has role ${user.role}" — repetición de user. En el segundo ejemplo with agrupa la configuración de TextView: todos los métodos y propiedades se llaman sobre un mismo objeto sin repetición. Inconveniente de with — imposibilidad de usarlo en una cadena, ya que no es una extensión y no devuelve el objeto de contexto.
Preguntas frecuentes
Use apply — devuelve el objeto y proporciona acceso mediante this, permitiendo establecer propiedades sin repetir el nombre del objeto. also es adecuado si se necesita un parámetro it explícito para distinguirlo de las propiedades del objeto.
let pasa el contexto mediante it (parámetro explícito), run — mediante this (receptor implícito). let es conveniente para verificaciones null y transformaciones, run — para cálculos con contexto. Ambas devuelven el resultado de la lambda.
with no es una extensión — toma el objeto como argumento. Use with para agrupar llamadas cuando no se necesita encadenamiento. apply es una extensión que devuelve el objeto, conveniente en inicializadores y patrones builder.
Sí, pero tenga cuidado con this — this anidado crea confusión. Use let o also con nombres explícitos para contextos anidados y evitar ambigüedad al referirse al objeto externo.
Todas las scope functions son inline, por lo que no generan sobrecarga de clases anónimas. La diferencia de rendimiento entre ellas es insignificante. La elección debe basarse en la legibilidad y las convenciones, no en la velocidad.
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