extension property — un mecanismo de Kotlin que permite agregar nuevas propiedades a clases existentes sin herencia y sin modificar el código fuente. A diferencia de las extension functions, las extension properties no pueden almacenar estado — se declaran solo con un getter y, opcionalmente, un setter, ya que no tienen un backing field. Según Kotlin Documentation, 2025, las extension properties se compilan en métodos estáticos getter y setter con el receiver como primer parámetro.
Puntos clave
Una extension property es una construcción sintáctica de Kotlin que agrega una propiedad a un tipo existente sin cambiar su declaración. La propiedad se declara con un receiver type y debe contener un getter. La diferencia clave con las propiedades normales es la ausencia de un backing field: una extension property no puede almacenar datos, solo calcularlos basándose en el objeto receiver.
Según la Encuesta de Kotlin Foundation (2024), las extension properties son menos populares que las extension functions — aproximadamente el 45% de los desarrolladores las usan regularmente. Esto se debe a la limitación de no tener estado, lo que reduce el ámbito de aplicación. Sin embargo, para propiedades calculadas que están lógicamente vinculadas a un tipo, las extension properties son la opción más concisa.
Las extension properties se compilan en un par de métodos estáticos getter y setter. A nivel de bytecode, no hay diferencia entre llamar a una extension property y llamar a una extension function — ambas se convierten en métodos estáticos con un parámetro receiver. Según JetBrains (Kotlin Docs, 2025), no hay ninguna sobrecarga.
Use extension properties para valores calculados cortos que deben verse como propiedades en lugar de llamadas a métodos — esto mejora la legibilidad del código y cumple con el Principio de Acceso Uniforme.
Para declarar una extension property, use una sintaxis similar a la de una propiedad normal, pero con el prefijo del receiver type. val declara una extension property de solo lectura con un getter obligatorio, var declara una mutable con un getter y un setter opcional.
// Read-only extension property
val String.isEmail: Boolean
get() = this.contains("@") && this.contains(".")
// Call
val valid = "test@test.com".isEmail
Nota: una extension property se llama sin paréntesis — str.isEmail, no str.isEmail(). Esta es la diferencia clave entre una extension property y una extension function: una propiedad parece un campo, aunque en realidad se calcula a través de un getter.
Las extension properties pueden ser genéricas — el receiver puede usar parámetros de tipo genérico. Esto permite crear propiedades universales que funcionan con cualquier tipo de colección.
val List<T>.secondOrNull: T?
get() = if (size >= 2) this[1] else null
val items = listOf("a", "b", "c")
val second = items.secondOrNull // "b"
La propiedad secondOrNull funciona para cualquier tipo T, devolviendo el segundo elemento de la lista o null si hay menos de dos elementos. Este es un ejemplo típico donde una extension property es más apropiada que una función — su acceso parece la lectura de un campo.
Una extension property no puede tener un backing field porque no se agrega a los metadatos de la clase — existe solo como un par de funciones estáticas getter/setter. Backing field (la palabra clave field en Kotlin) es un campo interno de la clase que almacena el valor de la propiedad. Una extension property no tiene acceso a la estructura interna de la clase.
// ❌ ERROR: extension property cannot have a backing field
var String.cachedValue: String
get() = "computed"
set(value) {
field = value // field is not accessible!
}
// ✅ CORRECT: use external storage
val cache = MutableMap<String, String>()
var String.cachedValue: String
get() = cache[this] ?: ""
set(value) { cache[this] = value }
Un Map externo en el ejemplo resuelve el problema de almacenamiento pero crea otro — una fuga de memoria. Los valores obtenidos a través de una extension property viven en el Map para siempre si no se limpian. Esta limitación hace que las extension properties no sean adecuadas para almacenar caché o datos temporales.
Para almacenamiento en caché, se recomienda usar un WeakHashMap o mecanismos con limpieza automática. JetBrains recomienda evitar el uso de var extension properties con almacenamiento externo en código de producción sin una gestión cuidadosa del ciclo de vida.
La elección entre una extension property y una extension function depende de la semántica: una propiedad describe una característica de un objeto, mientras que una función describe una acción. El Principio de Acceso Uniforme establece: el cliente no debe saber si un valor se calcula o se almacena. Si el valor se puede representar como una característica (longitud, tamaño, estado) — use una propiedad.
| Criterio | Extension property | Extension function |
|---|---|---|
| Llamada | Sin paréntesis: obj.property | Con paréntesis: obj.function() |
| Semántica | Característica, atributo | Acción, operación |
| Backing field | No compatible | No aplicable |
| Parámetros | Solo getter/setter | Cualquier parámetro |
| Rendimiento | Igual (método estático) | Igual (método estático) |
| Ejemplo | text.length | text.isEmail() |
La regla es simple: si la operación acepta parámetros — use una extension function. Si es un valor calculado simple sin parámetros — use una extension property. Según Android Architecture Guide (Google, 2025), se debe dar preferencia a las extension properties para el acceso a datos y a las extension functions para operaciones con efectos secundarios.
Una extension property con la palabra clave var admite un setter, pero sin la capacidad de almacenar un valor — el setter generalmente realiza un efecto secundario o guarda datos en un almacenamiento externo. La sintaxis es similar a las propiedades mutables de clase.
// Mutable extension property with setter
var StringBuilder.lastChar: Char
get() = this[length - 1]
set(value) {
this.setCharAt(length - 1, value)
}
val sb = StringBuilder("Kotlin")
println(sb.lastChar) // n
sb.lastChar = '!'
println(sb) // Kotli!
La propiedad lastChar es un ejemplo clásico de la documentación de Kotlin. El getter devuelve el último carácter de StringBuilder, el setter lo reemplaza con un nuevo valor. Nota: el estado se almacena en el propio StringBuilder (a través de setCharAt), no en un campo separado — este es un uso correcto de una extension property.
En proyectos reales, las extension properties se usan más a menudo para simplificar el acceso a datos de colecciones, calcular tamaños o estados de elementos de UI, y crear una API conveniente sobre clases existentes. La biblioteca estándar de Kotlin usa activamente este mecanismo: size, indices, lastIndex para colecciones son extension properties.
// Extension properties for collections
val List<Int>.sumFast: Int
get() = fold(0) { acc, i -> acc + i }
val String.half: String
get() = this.substring(0, length / 2)
// Extension property for Android View
val View.isVisible: Boolean
get() = visibility == View.VISIBLE
// Null check via safe receiver
val String?.isNullOrBlank: Boolean
get() = this == null || this.isBlank()
La extension property isVisible para View es un ejemplo que todo desarrollador Android debería conocer. En lugar de view.visibility == View.VISIBLE, puede escribir view.isVisible. Esto no solo es más corto, sino que también se lee como lenguaje natural: "si la vista es visible". A pesar de su simplicidad, estas propiedades mejoran significativamente la legibilidad del código.
Preguntas frecuentes
No, las extension properties no se pueden declarar para un companion object o una declaración de object. El mecanismo de extensión se aplica solo a clases, interfaces y tipos anulables. Para object, use funciones regulares de nivel superior.
Una inline property (con el modificador inline) es un mecanismo de Kotlin para llamar a un getter/setter sin crear un objeto de propiedad. Una extension property siempre se compila en un método estático, mientras que una inline property se compila en una llamada sin envoltorio. Resuelven problemas diferentes: una extension property agrega una propiedad a un tipo existente, mientras que inline optimiza las llamadas a sus propias propiedades.
Sí, una extension property puede tener anotaciones, pero solo a nivel de declaración. No se puede anotar por separado el getter o el setter de una extension property — a diferencia de las propiedades de clase normales. Ejemplo: @JvmName("getIsValid") val String.isValid get() = true.
No, las extension properties no se pueden declarar con un companion object como receiver. Esta es una limitación del lenguaje — una extension property solo funciona con instancias de tipo, mientras que un companion object es un contexto estático. Use funciones de extensión de nivel superior o constantes.
Mínimamente. Cada extension property agrega un método getter estático (y opcionalmente un setter) al bytecode compilado. En comparación, crear una clase envoltorio con la misma propiedad agrega una clase completa. Las extension properties son un enfoque más ligero para extender la funcionalidad.
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