extension function — un mecanismo de Kotlin que permite añadir nuevos métodos a clases existentes sin herencia y sin modificar el código fuente. La función se declara con un prefijo en forma de tipo receptor y se llama como un método normal de ese tipo. Según Kotlin Documentation, 2025, las extension functions se compilan como métodos estáticos con el receptor como primer parámetro, lo que garantiza cero overhead en tiempo de ejecución en comparación con las llamadas normales.
Puntos clave
Una extension function es una función declarada fuera de una clase pero llamada como su método. Toma un tipo receptor al que se añade el nuevo método. Dentro de la función, la palabra clave this hace referencia a la instancia del tipo receptor. Este enfoque permite extender clases finales, clases de bibliotecas de terceros e incluso tipos primitivos sin crear envoltorios.
A diferencia de Java, donde para añadir un método a una clase existente se requiere herencia o una clase utilitaria estática, Kotlin ofrece una solución elegante sin boilerplate. Según un estudio de Kotlin Foundation (2024), las extension functions se encuentran entre las cinco características más utilizadas del lenguaje: el 78% de los desarrolladores Kotlin las utilizan en su trabajo diario.
El compilador de Kotlin traduce una extension function a un método estático donde la instancia del receptor se pasa como primer argumento. Esto significa que las extension functions no modifican el bytecode de la clase destino ni rompen la encapsulación — solo son accesibles los campos y métodos públicos del receptor.
Utilice extension functions para escribir funciones utilitarias limpias que estén lógicamente vinculadas a un tipo específico pero que no puedan añadirse a su código fuente.
La sintaxis básica de una extension function: el nombre del tipo receptor, un punto, el nombre de la función, luego los parámetros y el tipo de retorno. Dentro de la función, el receptor es accesible mediante this.
// Extension function for String
fun String.isEmail(): Boolean {
return this.contains("@") && this.contains(".")
}
// Called as regular String method
val result = "user@example.com".isEmail() // true
En este ejemplo, isEmail() está disponible para todas las cadenas. Dentro de la función, this se refiere a la cadena sobre la que se llama el método. Kotlin permite omitir this en la mayoría de los casos — se puede escribir simplemente contains("@") en lugar de this.contains("@").
Las extension functions pueden aceptar parámetros adicionales y devolver valores de cualquier tipo. Esto las convierte en funciones completas, no solo en azúcar sintáctico.
fun List<Int>.defaultIfEmpty(default: Int): List<Int> {
if (this.isEmpty()) return listOf(default)
return this
}
val populated = listOf(1, 2).defaultIfEmpty(0)
val empty = listOf<Int>().defaultIfEmpty(0)
El parámetro default define el valor devuelto para una lista vacía. Una extension function puede ser genérica — el tipo List
Las extension functions en Kotlin utilizan despacho estático, no despacho virtual. Esta es una diferencia clave con los métodos de clase normales. Qué extension function se llama se determina en tiempo de compilación por el tipo estático de la variable, no por su tipo real en tiempo de ejecución.
open class Animal
class Dog : Animal()
fun Animal.speak() = "Animal sound"
fun Dog.speak() = "Woof"
fun test() {
val animal: Animal = Dog()
println(animal.speak()) // "Animal sound" — static type Animal
val dog: Dog = Dog()
println(dog.speak()) // "Woof" — static type Dog
}
Aunque la variable animal apunta a una instancia de Dog, se llama a la extension function para Animal porque el tipo estático de la variable es Animal. Si speak() fuera un método virtual de clase, se habría llamado a Dog.speak(). Este comportamiento es importante tenerlo en cuenta al diseñar API con extension functions.
Las extension functions no son miembros de la clase — son funciones normales que requieren una importación para usarse en otro archivo. Kotlin proporciona dos opciones de importación: por nombre de función o con renombrado.
// Import by name
import com.example.extensions.isEmail
// Import with alias (to resolve conflicts)
import com.example.extensions.isEmail as isValidEmail
// Call after import
val valid = "test@test.com".isEmail()
El renombrado mediante as es útil para conflictos de nombres — por ejemplo, si dos bibliotecas proporcionan una extension function con el mismo nombre para el mismo tipo. En este caso, puede importar una de ellas con un nombre diferente y llamarla usando el nuevo nombre.
Las extension functions pueden declararse en diferentes niveles:
| Nivel | Visibilidad | Ejemplo |
|---|---|---|
| Top-level | Todo el proyecto después de importar | fun String.isEmail() |
| Member extension | Dentro de la clase propietaria | class A { fun B.ext() } |
| Local | Dentro de una función | fun test() { fun String.ext() } |
Las member extension functions son un caso especial donde una extension function se declara dentro de otra clase. En este caso, tanto el receptor (this de la función) como los miembros de la clase externa son accesibles dentro de la extension function.
Si una clase tiene un método con la misma firma que una extension function, siempre se llama al miembro de la clase. Una extension function nunca sobrescribe un método de clase — esta es una decisión arquitectónica para evitar sobrescrituras accidentales.
class User {
fun greet() = "Hello from class"
}
fun User.greet() = "Hello from extension"
fun main() {
val user = User()
println(user.greet()) // "Hello from class"
}
Incluso si la extension function se define más tarde y tiene la misma firma, el compilador elegirá el método de la clase. El compilador de Kotlin emite una advertencia cuando encuentra esta situación. La única forma de llamar a la extension function cuando existe un método de clase es invocarla como una función normal: greet(user).
En el desarrollo de Android, las extension functions se han convertido en una herramienta estándar para trabajar con View, Context y fragments. La biblioteca Android KTX está construida sobre extension functions, proporcionando envoltorios convenientes sobre la API de Android.
// Extension function for working with View
fun View.show() {
visibility = View.VISIBLE
}
fun View.hide() {
visibility = View.GONE
}
// Extension function for Context — toast
fun Context.toast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
// Extension function as DSL builder
fun String.colorFormat(color: String): String =
"$color$this\u001b[0m"
La extension function toast() hace que las llamadas dependientes de Context sean concisas: en lugar de escribir Toast.makeText(context, message, length).show(), basta con escribir context.toast("Texto"). Esto reduce el boilerplate y hace el código más legible.
Preguntas frecuentes
No, las extension functions utilizan despacho estático. Si declara una extension function para una clase base y la misma para una subclase, cuál se llama se determina por el tipo estático de la variable en tiempo de compilación, no por el tipo real en tiempo de ejecución.
Puede declarar extension functions para tipos receptores nullable: fun String?.isNullOrEmail(). Dentro de esta función, this puede ser null, por lo que debe usar operadores de llamada segura o comprobaciones explícitas de null.
No, las extension functions se compilan como métodos estáticos. A nivel de bytecode, llamar a una extension function es idéntico a llamar a un método estático con el receptor como primer parámetro. No implica reflexión ni despacho dinámico.
Sí, las extension properties funcionan de manera similar a las extension functions pero no pueden almacenar estado — solo un getter y un setter. Por ejemplo: val List
En Java se escribe StringUtils.isEmail(str), mientras que en Kotlin se escribe str.isEmail(). La diferencia no es solo sintáctica: las extension functions soportan autocompletado en IDE, mejoran la legibilidad de las cadenas de llamadas y permiten que el IDE sugiera funciones relevantes para un tipo específico. Java no tiene estas capacidades.
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