Advertisement
  1. Code
  2. Kotlin

Kotlin desde cero: Más diversión con funciones

by
Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called Kotlin From Scratch.
Kotlin From Scratch: Packages and Basic Functions
Kotlin From Scratch: Advanced Functions

Spanish (Español) translation by Javier Salesi (you can also view the original English article)

Kotlin es un moderno lenguaje de programación que se compila en el bytecode Java. Es gratuito y de código abierto y promete hacer más divertida la codificación para Android.

En el artículo previo, aprendiste sobre los paquetes y las funciones básicas en Kotlin. Las funciones son la parte medular de Kotlin, así que en este artículo las veremos más detenidamente. Exploraremos las siguientes clases de funciones en Kotlin:

  • funciones de alto nivel
  • expresiones lambda o funciones sin un nombre
  • funciones anónimas
  • funciones locales o anidadas
  • funciones infix
  • funciones miembro

¡Estarás sorprendido por todas las cosas geniales que puedes hacer con funciones en Kotlin!

1. Funciones de alto nivel

Las funciones de alto nivel son funciones dentro de un paquete de Kotlin que son definidas afuera de cualquier clase, objeto o interfaz. Esto significa que son funciones que llamas directamente, sin la necesidad de crear algún objeto o llamar a cualquier clase.

Si programas en Java, sabes que típicamente creamos métodos estáticos dentro de clases auxiliares. Estas clases auxiliares en realidad no hacen nada, no tienen ningún estado o métodos de instancia y solo actúan como un contenedor para los métodos estáticos. Un ejemplo típico es la clase Collections en el paquete java.util y sus métodos estáticos.

Las funciones de alto nivel en Kotlin pueden usarse como remplazo para los métodos estáticos utilitarios dentro de clases auxiliares que codificamos en Java. Veamos cómo definir una función de alto nivel en Kotlin.

En el código que se muestra arriba, definimos un paquete com.chikekotlin.projectx.utils dentro de un archivo llamado UserUtils.kt y también definimos una función utilitaria de alto nivel llamada checkUserStatus(), dentro de este mismo paquete y archivo. Para no extendernos, esta función sencilla devuelve la cadena o string "online".

Lo siguiente que haremos será usar esta función utilitaria en otro paquete o archivo.

En el código precedente, importamos la función en otro paquete ¡y luego lo ejecutamos! Como puedes ver, no tenemos que crear un objeto o hacer referencia a una clase para llamar a esta función.

Interoperabilidad con java

Dado que Java no soporta funciones de alto nivel, el compilador de Kotlin creará tras bambalinas una clase Java y las funciones individuales de alto nivel se convertirán en métodos estáticos. En nuestro caso, la clase Java generada fue UserUtilsKt con un método estático checkUserStatus().

Esto significa que los invocadores Java simplemente llaman al método haciendo referencia a su clase generada, igual que para cualquier otro método estático.

Nota que cambiamos el nombre de la clase Java que genera el compilador de Kotlin usando la anotación @JvmName.

En el código observado arriba, aplicamos la anotación @JvmName y especificamos un nombre de clase UserUtils para el archivo generado. Nota también que esta anotación es colocada al inicio del archivo Kotlin, antes de la definición del paquete.

Puede ser referenciado desde Java de esta manera:

2. Expresiones lambda

Las expresiones lambda (o funciones sin nombre) tampoco están vinculadas a cualquier entidad como una clase, objeto o interfaz. Pueden pasarse como argumentos a otras funciones llamadas funciones de orden superior (las discutiremos en el siguiente artículo). Una expresión lambda representa solo el bloque de una función y usándolas reduce el ruido en nuestro código.

Si eres un programador en Java, sabes que Java 8 y versiones superiores proporcionan soporte para expresiones lambda. Para usarlas en un proyecto que soporte versiones anteriores de Java como Java 7, 6 o 5, podemos usar la popular biblioteca Retrolambda.

Una de las cosas fabulosas de Kotlin es que las expresiones Lambda son soportadas al instante. Debido a que lambda no es soportada en Java 6 o 7, para que Kotlin interopere con ella, Kotlin crea una clase anónima de Java tras bambalinas. Pero nota que crear una expresión lambda en Kotlin es muy diferente respecto a cómo se crea en Java.

Estas son las características de una expresión lambda en Kotlin:

  • Debe escribirse entre llaves {}.
  • No tiene la palabra reservada fun.
  • No hay modificador de acceso (privado, público o protegido) porque no pertenece a ninguna clase, objeto o interfaz.
  • No tiene un nombre de función. En otras palabras es anónima.
  • Ningún tipo de devolución es especificado porque será deducido por el compilador.
  • Los parámetros no se escriben entre paréntesis ().

Además, podemos asignar una expresión lambda a una variable y luego ejecutarla.

Creando expresiones Lambda

Veamos ahora algunos ejemplos de expresiones lambda. En el código mostrado a continuación, creamos una expresión lambda sin ningún parámetro y la asignamos a una variable message. Luego ejecutamos la expresión lambda llamando a message().

También veamos cómo incluir parámetros en una expresión lambda.

En el código de arriba, creamos una expresión lambda con el parámetro myString, junto con el tipo de parámetro String. Como puedes ver, junto al tipo de parámetro, hay una flecha: esto hace referencia al cuerpo lambda. En otras palabras, esta flecha separa la lista de parámetros del cuerpo lambda. Para hacerlo más conciso, podemos ignorar completamente el tipo de parámetro (ya deducido por el compilador).

Para tener múltiples parámetros, solo los separamos con una coma. Y recuerda, no escribimos la lista de parámetros entre paréntesis al igual que en Java.

Sin embargo, nota que si los tipos de parámetros no pueden deducirse, deben especificarse explícitamente (como en este ejemplo), de otra manera el código no se compilará.

Pasando lambdas a funciones

Podemos pasar expresiones lambda como parámetros a funciones. Se llaman "funciones de orden superior", porque son funciones de funciones. Estas clases de funciones pueden aceptar una lambda o una función anónima como parámetro: por ejemplo, la función de colección last().

En el código observado enseguida, pasamos una expresión lambda a la función last(). (Si quieres un recordatorio sobre colecciones en Kotlin, visita el tercer tutorial de esta serie). Como el nombre lo indica, devuelve el último elemento de la lista. last() acepta una expresión lambda como parámetro y esta expresión a cambio toma un argumento de tipo String. El cuerpo de su función sirve como predicado para buscar dentro de un subconjunto de elementos en la colección. Ello significa que la expresión lambda decidirá qué elementos de la colección serán considerados cuando se busque el último.

Veamos cómo hacer más legible la última línea del código observado arriba.

El compilador de Kotlin nos permite eliminar los paréntesis de la función si el último argumento en la función es una expresión lambda. Como puedes observar en el código de arriba, se nos permitió hacer esto porque el último y único argumento pasado a la función last() es una expresión lambda.

Es más, podemos hacerlo más conciso eliminando el tipo de parámetro.

No necesitamos especificar explícitamente el tipo de parámetro porque siempre es el mismo que el tipo de elemento de la colección. En el código observado arriba, llamamos a last en una colección de lista de objetos String, así que el compilador de Kotlin es lo suficientemente inteligente para saber que el parámetro también será de tipo String.

El nombre de argumento it

Incluso podemos simplificar más la expresión lambda de nuevo remplazando el argumento de la expresión lambda con el nombre del argumento predeterminado generado automáticamente it.

El nombre de argumento it se generó automáticamente porque last puede aceptar una expresión lambda o una función anónima (lo veremos en breve) con solo un argumento, y su tipo puede ser deducido por el compilador.

Devolución local en expresiones lambda

Comencemos con un ejemplo. En el código observado a continuación, pasamos una expresión lambda a la función foreach() invocada en la colección intList. Esta función iterará por la colección y ejecutará la lambda en cada elemento de la lista. Si cualquier elemento es divisible entre 2, se detendrá y devolverá desde la lambda.

Ejecutando el código de arriba podrías tener un resultado no esperado. Esto se debe a que la declaración de devolución no devolverá desde la lambda ¡sino desde la función contenedora surroundingFunction()! Esto significa que la última declaración de código en surroundingFunction() no se ejecutará.

Para arreglar este problema, necesitamos decirle explícitamente desde cuál función devolver usando una etiqueta o etiqueta de nombre.

En el código actualizado observado arriba, especificamos la etiqueta predeterminada @forEach inmediatamente después de la palabra reservada return dentro de la lambda. Ahora hemos instruido al compilador a que devuelva desde la lambda en lugar de que devuelva desde la función contenedora surroundingFunction(). Ahora la última declaración de surroundingFunction() se ejecutará.

Nota que también podemos definir nuestra etiqueta o etiqueta de nombre.

En el código observado arriba, definimos nuestra etiqueta personalizada llamada myLabel@ y luego la especificamos para la palabra clave return. La etiqueta @forEach generada por el compilador para la función forEach ya no está disponible porque hemos definido la nuestra.

Sin embargo, pronto verás cómo este problema de devolución local puede resolverse sin etiquetas cuando discutamos en breve las funciones anónimas en Kotlin.

3. Funciones miembro

Esta clase de función se define dentro de una clase, objeto o interfaz. Usando funciones miembro nos ayudan a modularizar más nuestros programas. Veamos ahora cómo crear una función miembro.

Este fragmento de código muestra una clase Circle (discutiremos las clases de Kotlin en artículos futuros) que tiene una función miembro calculateArea(). Esta función toma un parámetro radius para calcular el área de un círculo.

Para invocar una función miembro, usamos el nombre de la clase contenedora o instancia del objeto con un punto, seguido por el nombre de la función, pasando cualquier argumento si es necesario.

4. Funciones anónimas

Una función anónima es otra manera de definir un bloque de código que puede ser pasado a una función. No está vinculado a cualquier identificador. Estas son las características de una función anónima en Kotlin.

  • no tiene nombre
  • se crea con la palabra reservada fun
  • contiene un cuerpo de función

Debido a que pasamos una lambda a la función last() arriba, no podemos ser explícitos sobre el tipo de devolución. Para ser explícito sobre el tipo de devolución, necesitamos usar mejor una función anónima.

En el código de arriba, hemos remplazado la expresión lambda con una función anónima porque queremos ser explícitos sobre el tipo de devolución.

Hacia el final de la sección de lambda en este tutorial, utilizamos una etiqueta para especificar desde qué funcionar devolver. En cambio usando una función anónima de una lambda dentro de la función forEach() resuelve este problema de una manera más sencilla. La expresión return devuelve desde la función anónima y no de la que la incluye, que en nuestro caso es surroundingFunction().

5. Funciones locales o anidadas

Para llevar más lejos la modularización del programa, Kotlin nos proporciona funciones locales, también conocidas como funciones anidadas. Una función local se declara dentro de otra función.

Como puedes observar en el fragmento de código de arriba, tenemos dos funciones de una sola línea: calCircumference() y CalArea() anidadas dentro de la función printCircumferenceAndAread(). Las funciones anidadas pueden llamarse únicamente desde dentro de la función que la contiene y no desde afuera. De nuevo, el uso de las funciones anidadas hace a nuestro programa más modular y organizado.

Podemos hacer más concisas nuestras funciones locales no pasándoles parámetros explícitamente. Esto es posible porque las funciones locales tienen acceso a todos los parámetros y a las variables de la función que la incluye. Veamos ahora eso en acción.

Como puedes ver, este código actualizado luce más legible y reduce el ruido que teníamos. Aunque la función interna en este ejemplo es pequeña, en una función interna más grande puede ser segmentada en funciones anidadas más pequeñas, esta característica en verdad puede resultar útil.

6. Funciones infix

La notación infix nos permite llamar fácilmente a una función miembro de un argumento o función extensión. Además de una función de un argumento, también debes definir la función usando el modificador infix. Para crear una función infix, están involucrados dos parámetros. El primer parámetro es el objeto objetivo, mientras que el segundo parámetro es un único parámetro pasado a la función.

Creando una función miembro infix

Veamos cómo crear una función infix en una clase. En el ejemplo de código mostrado a continuación, creamos una clase Student con un campo de instancia mutable kotlinScore. Creamos una función infix usando el modificador infix antes de la palabra reservada fun. Como puedes ver abajo, creamos una función infix addKotlinScore() que toma una puntuación y la suma al campo de instancia kotlinScore.

Llamando a una función infix

Veamos también cómo invocar a la función infix que hemos creado. Para llamar a una función infix en Kotlin, no necesitamos usar la notación de punto y no necesitamos encerrar el parámetro entre paréntesis.

En el código de arriba, llamamos a la función infix, el objeto objetivo es student y el doble 95.00 es el parámetro pasado a la función.

Usar las funciones infix de manera inteligente puede hacer nuestro código más expresivo y más claro que en el estilo normal. Esto es muy apreciado cuando escribes pruebas de unidad en Kotlin (discutiremos testeo en Kotlin en un artículo posterior).

La función infix to

En Kotlin, podemos hacer la creación de una instancia Pair más concisa usando la función infix to en lugar del constructor Pair. (Tras bambalinas, to también crea una instancia Pair. Nota que la función to también es una función extensión (ahondaremos sobre esto en un artículo futuro).

Ahora comparemos la creación de una instancia Pair usando la función infix to y usando directamente el constructor Pair que ejecuta la misma operación y ve cuál es la mejor.

Como puedes ver en el código mostrado arriba, usar la función infix to es más conciso que usar directamente el constructor Pair para crear una instancia Pair. Recuerda que usar la función infix to, 234 es el objeto objetivo y el String "Nigeria" es el parámetro pasado a la función. Además, nota que también podemos hacer esto para crear un tipo Pair:

En el artículo Rangos y colecciones, creamos una colección de mapas en Kotlin dándole una lista de pares, el primer valor es la clave y el segundo el valor. También comparemos la creación de un mapa usando la función infix to y el constructor Pair para crear los pares individuales.

En el código de arriba, creamos una lista separada por comas de tipos Pair usando la función infix to y los pasamos a la función mapOf(). También podemos crear el mismo mapa usando directamente el constructor Pair para cada par.

Como puedes ver de nuevo, apegarse a la función infix to tiene menos ruido que usar el constructor Pair.

Conclusión

En este tutorial aprendiste sobre algunas de las sensacionales cosas que puedes hacer con funciones en Kotlin. Abordamos:

  • funciones de alto nivel
  • expresiones lambda o funciones sin nombre
  • funciones miembro
  • funciones anónimas
  • funciones locales o anidadas
  • funciones infix

¡Pero eso no es todo! Todavía hay más por aprender sobre funciones en Kotlin. Así que en el siguiente artículo, aprenderás algunos usos avanzados de las funciones como funciones extensión, funciones de orden superior y cierres. ¡Nos vemos pronto!

Para aprender más sobre el lenguaje Kotlin, te recomiendo visitar la documentación de Kotlin. O consulta algunos otros de nuestros artículos sobre desarrollo de aplicaciones en Android ¡aquí en Envato Tuts+!

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.