Una de las bondades de Kotlin más comentadas cuando salió fueron las funciones de extensión y en este capítulo te voy a enseñar a usarlas y además te mostraré algunas muy útiles que uso en mi día a día.

Las funciones de extensión nos permiten crear nuevas funciones a partir de un tipo de clase, como por ejemplo una String. La gracia de esto es que evitamos tener que tocar dichas clases y atomizar las funciones para que realicen una única acción.

Crear funciones de extensión en Kotlin

Vamos con un primer ejemplo para entenderlo. En cualquiera de tus proyectos crea una nueva clase llamada Extensions. Cara al futuro lo perfecto sería tener un directorio y dividirlas por tipo por ejemplo StringExt, DrawablesExt, etc.

class Extensions {
    
    fun String.happy():String{
        return "$this :)"
    }
    
}

Fíjate que a diferencia de una función normal, al ser una función de extensión lleva la clase de la que va a extender primero (String), seguido de un punto y el nombre de tu función. Esta función retornará una String, para ser exactos, el contenido original añadiendo una carita feliz al final.

Cuando usamos una función de extensión, la expresión this tendrá el contenido de ficha función, es decir si la función sale de un String, this contendrá un String, si por ejemplo la función sale de un drawable, this será un drawable y así con todo.

Ejemplos funciones de extensión

No entenderás as funciones hasta ponerte manos a la obra así que vamos con cinco ejemplos para que veas la potencia y versatilidad de esta característica.

Comprobar nulos

¿Cuántas veces has tenido que comprobar un null? Pues esta primera función es muy sencilla pero nos dejará el código más legible y al final eso es lo importante.

fun Any?.isNull() = this == null

Esta función extiende de Any, es decir, cualquier cosa y como puede ser null tiene que llevar la interrogación (?). Luego simplemente vamos a devolver si this (cualquier objeto con el cual usemos esta función de extensión) es null.

    val age:Int? = null
        
        if(age.isNull()){
            //Age es null
        }

Simplificar Toast

Este es uno de los ejemplos más habituales pero es que es realmente útil, yo luego tenerlo en la mayoría de proyectos. Para crear un Toast, es un proceso muy sencillo pero que implica mucho código.

        Toast.makeText(this, "Texto a mostrar", Toast.LENGTH_SHORT).show()

Vamos a simplificarlo con una función de extensión llamada toast().

fun Activity.toast(text: String, duration: Int = Toast.LENGTH_SHORT) {
    Toast.makeText(this, text, duration).show()
}

Creamos una función que extienda de Activity y recibirá un parámetro obligatorio (el texto a mostrar) y uno opcional, la longitud, ya que tiene un valor por defecto de tipo LENGTH_SHORT.

  toast("Ejemplo 1")
  toast("Ejemplo 2", Toast.LENGTH_LONG)

Recuperar un color

Este es el tipo de tarea repetitiva que genera boilerplate, es decir, código repetido que no aporta valor. Como norma general si quisiéramos recuperar un color tendríamos que repetir esta función por cada una de las peticiones.

ContextCompat.getColor(this, color)

Ahora en nuestro fichero de extensiones añadiremos una nueva función que he llamado color.

    fun Activity.color(@ColorRes color: Int) = ContextCompat.getColor(this, color)

Básicamente es una función que extiende de las Activities, por lo que podrás usarla en todas tus actividades y recibe por parámetro un Int (R.color.miColor), pero como queremos asegurarnos que no ponga un número aleatorio añadimos la etiqueta @ColorRes y así obligamos a que sea una referencia de un color de nuestro proyecto.

Ahora en nuestra Activity solo tendríamos que hacer lo siguiente.

        color(R.color.white)

Cargar URL en Kotlin con Glide

Obviamente podremos usar librerías en las funciones de extensión y para este ejemplo usaremos la famosa librería Glide. Por si no la conoces es una potente herramienta para cargar URLs en nuestros ImageViews.

Obviamente tendremos que meter las dependencias en nuestro build.gradle.

implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'

Ahora es el momento de ir a nuestro fichero Extensions.kt y crear la nueva función.

fun ImageView.load(url: String) {
    if (url.isNotEmpty()) {
        Glide.with(this.context).load(url).into(this)
    }
}

Extendemos de los ImageView, ya que serán los que puedan cargar las imágenes y recibirá dicha URLs por parámetro. Acto seguido comprobamos que la String no está vacía para tener poder evitar fallos en nuestra app y ahora sí llamamos a Glide. Fíjate que cuando necesita el contexto he puesto this.context, ya que al extender de un componente visual de la UI, este siempre tendrá contexto.

Simplificando listeners en Kotlin

Hay ocasiones en las que tenemos que implementar listeners enormes de los cuales solo queremos una única función como por ejemplo textChangedListener.

      editText.addTextChangedListener(object : TextWatcher {

            override fun afterTextChanged(s: Editable?) {
                //Se ha añadido un nuevo caracter al edit text
            }

            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
               //Se va a añadir un nuevo caracter al edit text
            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
               //Ya se ve el caracter en el edit text
            }
        })

En el ejemplo anterior estamos implementando textChangedListener(), por lo que tenemos que añadir las tres funciones. Son tan similares que la mayoría de las veces solo usarás una de ellas, así que vamos a simplificarlo.

fun EditText.onTextChanged(listener: (String) -> Unit) {
    this.addTextChangedListener(object : TextWatcher {

        override fun afterTextChanged(s: Editable?) {
            listener(s.toString())
        }

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
    })
}

Esta función extiende de un editText que son los que implementan este listener. Por parámetro recibirá una función que devuelva un String y creamos nuestro textChangedListener pero nuestra función sólo la añadimos en el método que queremos. Gracias a esto podemos simplificar la llamada en nuestra Activity.

        editText.onTextChanged { "El carácter añadido es $it" }

Mucho más legible ¿Verdad?

Espero que estos ejemplos te hayan servido para poder mejorar tu código como programador.

Continúa con el curso: Capítulo 37 – Polylines en Google Maps con Kotlin [Parte 3]


Te recuerdo que puedes seguirme en mis redes sociales en Aristi.Dev. Y si tienes dudas con este o cualquier otro artículo del blog únete al Discord de la comunidad y te ayudaremos.