ProgramandooIntentándolo

Funciones de extensión en Kotlin

reset css

Las funciones de extensión son una característica muy interesante de Kotlin que nos permite añadir funciones a cualquier clase aunque no tengamos acceso a su código fuente y sin tener que crear una clase que la extienda.

Para definir una función de extensión en Kotlin simplemente tenemos que poner el nombre de la clase para la que queremos crear la función fun Clase.funcion(...){//...}.

Vamos a verlo con un ejemplo muy sencillo, por ejemplo si queremos que la clase Int tenga un método multiplicar para que multiple el número sobre el que se hace la operación por el que le pasemos como parametro solo tenemos que definir la función como hacemos normalmente pero indicando delante el nombre de la clase.


fun Int.multiplicar(numero: Int): Int {
    return this * numero;
}

Dentro de la función tenemos acceso al objeto de la clase para el que creamos la función de extensión mediante this, y por lo tanto también podemos acceder a sus propiedades y métodos porque a efectos practicos es como si estuviésemos creando la función dentro de la clase.

Y ya podemos utilizarla como cualquier otra función de la clase, como por ejemplo Int.times() que es el método que ya tiene esta clase para multiplicar 😛.


fun main(args: Array<String>) {
    var multiplicacion = 20.multiplicar(5)
    println("El resultado de la multiplicacion es $multiplicacion == ${20.times(5)}")
    // El resultado de la multiplicacion es 100 == 100
}

También podemos crear funciones de extensión sobre listas para poder ejecutar directamente las operaciones sobre la lista en lugar de tener que crear una función que reciba la lista como uno de sus parámetros. Vamos con otro ejemplo muy sencillo de una función sobre MutableList<Int> que sume el número pasado como parámetro a cada uno de los elementos de la lista.


fun MutableList<Int>.sumar(numero: Int): MutableList {
    var numeros = mutableListOf<Int>()
    for (i in this) {
        numeros.add(i + numero)
    }

    return numeros
}

fun main(args: Array<String>) {
    var numeros: MutableList<Int> = mutableListOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    var resultado = numeros.sumar(2)

    println("La lista resultante es $resultado")
    // La lista resultante es [3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
}

Una cosa importanete a tener en cuenta es que las funciones de extensión no se pueden usar para sobreescribir funciones, es decir, que si creas una función de extensión con el mismo nombre y los mismos parametros que los de una función ya existente (da igual si el tipo de la respuesta es distinto) de la clase no se va a utilizar porque el compilador busca primero las funciones en la clase, en sus superclases y finalmente utiliza las funciones de extensión.

Por ejemplo si extendemos las funciones Int.times() y Int.div() para que multiplique por -1 el resultado y para que devuelva un String respectivamente vamos a conseguir lo mismo, que es… nada, porque no se van a llegar a ejecutar nunca.


fun Int.times(numero: Int): Int {
    return -1 * this * numero
}

fun Int.div(numero: Int): String {
    return "El resultado de la division es ${this / numero}"
}

fun main(args: Array<String>) {
    var multiplicacion = 20.times(5)
    println("El resultado de la multiplicacion es $multiplicacion")
    // El resultado de la multiplicacion es 100

    val division = 20.div(5)
    println(division)
    // 4
}

Aunque el IDE nos avisa (IntelliJ nos muestra el aviso “Extension is shadowed by a member: public final operator fun times(other: Int): Int”) si insistimos y creamos la función de todos modos el resultado es que no se ejecutan nuestras funciones de extensión, así que hay que tener un poco de cuidado para no volvernos locos porque se ejecute algo distinto a lo que esperamos.

Por lo tanto si se quiere usar una función de extensión y ya existe esa función en la clase no nos queda más remedio que utilizar otro nombre para la función o extender la clase y sobrescribir ese método.