Kotlin From Scratch Más diversión con funciones

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

En el artículo anterior, aprendiste sobre paquetes y funciones básicas en Kotlin. Las funciones están en el corazón de Kotlin, por lo que en esta publicación las veremos más de cerca. Exploraremos los siguientes tipos de funciones en Kotlin:

  • funciones de nivel superior
  • Expresiones lambda o funciones literales.
  • funciones anonimas
  • funciones locales o anidadas
  • funciones de infijo
  • funciones miembro

Te sorprenderás de todas las cosas geniales que puedes hacer con las funciones en Kotlin!

1. Funciones de nivel superior

Las funciones de nivel superior son funciones dentro de un paquete de Kotlin que se definen fuera de cualquier clase, objeto o interfaz. Esto significa que son funciones a las que llama directamente, sin la necesidad de crear ningún objeto o llamar a ninguna clase. 

Si eres un codificador Java, sabes que normalmente creamos métodos estáticos de utilidad dentro de las clases auxiliares. Estas clases auxiliares realmente no hacen nada; no tienen ningún método de estado o instancia, y simplemente actúan como un contenedor para los métodos estáticos. Un ejemplo típico es el Colecciones clase en el java.util paquete y sus métodos estáticos. 

Las funciones de nivel superior en Kotlin se pueden usar como un reemplazo para los métodos de utilidad estática dentro de las clases auxiliares que codificamos en Java. Veamos cómo definir una función de nivel superior en Kotlin. 

paquete com.chikekotlin.projectx.utils fun checkUserStatus (): String return "online"

En el código anterior, definimos un paquete. com.chikekotlin.projectx.utils dentro de un archivo llamado UserUtils.kt y también se define una función de utilidad de nivel superior llamada checkUserStatus () Dentro de este mismo paquete y archivo. Para mayor brevedad, esta función muy simple devuelve la cadena "en línea". 

Lo siguiente que haremos es usar esta función de utilidad en otro paquete o archivo.

paquete com.chikekotlin.projectx.users import com.chikekotlin.projectx.utils.checkUserStatus if (checkUserStatus () == "online") // haz algo

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

Interoperabilidad de Java

Dado que Java no admite funciones de nivel superior, el compilador Kotlin detrás de escena creará una clase Java y las funciones de nivel superior individuales se convertirán a métodos estáticos. En nuestro caso, la clase Java generada fue UserUtilsKt con un método estático checkUserStatus ()

/ * Java * / package com.chikekotlin.projectx.utils public class UserUtilsKt public static String checkUserStatus () return "online"; 

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

/ * Java * / import com.chikekotlin.projectx.utils.UserUtilsKt ... UserUtilsKt.checkUserStatus ()

Tenga en cuenta que podemos cambiar el nombre de la clase Java que genera el compilador Kotlin usando el @JvmName anotación.

@file: JvmName ("UserUtils") paquete com.chikekotlin.projectx.utils divertido checkUserStatus (): String return "online"

En el código anterior, aplicamos la @JvmName anotación y especificó un nombre de clase UserUtilspara el archivo generado. Tenga en cuenta también que esta anotación se coloca al principio del archivo Kotlin, antes de la definición del paquete.. 

Puede ser referenciado desde Java de esta manera:

/ * Java * / import com.chikekotlin.projectx.utils.UserUtils… UserUtils.checkUserStatus ()

2. Expresiones Lambda

Las expresiones Lambda (o funciones literales) tampoco están vinculadas a ninguna entidad, como una clase, un objeto o una interfaz. Se pueden pasar como argumentos a otras funciones llamadas funciones de orden superior (las analizaremos más en la próxima publicación). Una expresión lambda representa solo el bloque de una función, y su uso reduce el ruido en nuestro código. 

Si usted es un codificador de Java, sabe que Java 8 y superior proporciona soporte para las expresiones lambda. Para usar expresiones lambda en un proyecto que admita versiones anteriores de Java como Java 7, 6 o 5, podemos usar la popular biblioteca Retrolambda. 

Una de las cosas asombrosas de Kotlin es que las expresiones lambda son compatibles fuera de la caja. Debido a que lambda no es compatible con Java 6 o 7, para que Kotlin pueda interactuar con él, Kotlin crea una clase anónima de Java detrás de la escena. Pero tenga en cuenta que crear una expresión lambda en Kotlin es bastante diferente de lo que es en Java.

Aquí están las características de una expresión lambda en Kotlin:

  • Debe estar rodeado de tirantes .
  • No tiene la divertido palabra clave. 
  • No hay modificador de acceso (privado, público o protegido) porque no pertenece a ninguna clase, objeto o interfaz.
  • No tiene nombre de función. En otras palabras, es anónimo.. 
  • No se especifica ningún tipo de retorno porque será compilado por el compilador.
  • Los parámetros no están entre paréntesis ()

Y, lo que es más, podemos asignar una expresión lambda a una variable y luego ejecutarla. 

Creación de expresiones Lambda

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

val message = println ("¡Hey, Kotlin es realmente genial!") message () // "¡Hey, Kotlin es realmente genial!"

Veamos también cómo incluir parámetros en una expresión lambda.. 

val message = myString: String -> println (myString) message ("I love Kotlin") // "I love Kotlin" message ("¿Qué tan lejos?") // "¿Qué tan lejos?"

En el código anterior, creamos una expresión lambda con el parámetro myString, junto con el tipo de parámetro Cuerda. Como puede ver, delante del tipo de parámetro, hay una flecha: esto se refiere 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). 

val message = myString -> println (myString) // todavía se compilará

Para tener múltiples parámetros, simplemente los separamos con una coma. Y recuerde, no envolvemos la lista de parámetros entre paréntesis como en Java. 

val addNumbers = number1: Int, number2: Int -> println ("Agregar $ number1 y $ number2") resultado de val = number1 + number2 println ("El resultado es $ result") addNumbers (1, 3)

Sin embargo, tenga en cuenta que si los tipos de parámetros no pueden inferirse, deben especificarse explícitamente (como en este ejemplo), de lo contrario el código no se compilará.

Sumando 1 y 3 El resultado es 4.

Pasando Lambdas a Funciones

Podemos pasar las expresiones lambda como parámetros a las funciones: se denominan "funciones de orden superior", porque son funciones de funciones. Este tipo de funciones pueden aceptar una función lambda o anónima como parámetro: por ejemplo, la último() función de recolección. 

En el siguiente código, pasamos en una expresión lambda a la último() función. (Si desea actualizar las colecciones en Kotlin, visite el tercer tutorial de esta serie) Como su nombre lo indica, devuelve el último elemento de la lista.  último() acepta una expresión lambda como parámetro, y esta expresión a su vez toma un argumento de tipo Cuerda. El cuerpo de su función sirve como predicado para buscar dentro de un subconjunto de elementos en la colección. Eso significa que la expresión lambda decidirá qué elementos de la colección se considerarán al buscar el último.

val stringList: lista = listOf ("in", "the", "club") print (stringList.last ()) // imprimirá "club" print (stringList.last (s: String -> s.length == 3) ) // imprimirá "el"

Veamos cómo hacer que la última línea de código de arriba sea más legible..

stringList.last s: String -> s.length == 3 // también compilará e imprimirá "the" 

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

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

stringList.last s -> s.length == 3 // también compilará la impresión "the" 

No necesitamos especificar el tipo de parámetro explícitamente, porque el tipo de parámetro es siempre el mismo que el tipo de elemento de colección. En el código anterior, estamos llamando último en una lista de colección de Cuerda objetos, por lo que el compilador Kotlin es lo suficientemente inteligente como para saber que el parámetro también será un Cuerda tipo. 

los eso Nombre del argumento

Incluso podemos simplificar aún más la expresión lambda reemplazando el argumento de la expresión lambda con el nombre de argumento predeterminado generado automáticamente eso.

stringList.last it.length == 3

los eso nombre del argumento fue generado automáticamente porque último puede aceptar una expresión lambda o una función anónima (llegaremos a eso en breve) con un solo argumento, y su compilador puede inferir su tipo.  

Regreso local en expresiones Lambda

Vamos a empezar con un ejemplo. En el siguiente código, pasamos una expresión lambda a la para cada() función invocada en el intList colección. Esta función recorrerá la colección y ejecutará la lambda en cada elemento de la lista. Si cualquier elemento es divisible por 2, se detendrá y regresará de la lambda.. 

fun surroundingFunction () val intList = listOf (1, 2, 3, 4, 5) intList.forEach if (it% 2 == 0) return println ("End of surroundingFunction ()") surroundingFunction ( ) // no pasó nada

Es posible que la ejecución del código anterior no le haya dado el resultado que esperaba. Esto se debe a que la declaración de retorno no se devolverá desde la lambda, sino desde la función que lo contiene alrededoresFunción ()! Esto significa que la última declaración de código en el alrededoresFunción () no ejecutará. 

// ... println ("End of surroundingFunction ()") // Esto no se ejecutará // ... 

Para solucionar este problema, debemos decirle explícitamente de qué función se va a devolver utilizando una etiqueta o etiqueta de nombre. 

fun surroundingFunction () val intList = listOf (1, 2, 3, 4, 5) intList.forEach if (it% 2 == 0) return @ forEach println ("End of surroundingFunction ()") / / Ahora ejecutará surroundingFunction () // print "End of surroundingFunction ()"

En el código actualizado anterior, especificamos la etiqueta predeterminada @para cada inmediatamente después de la regreso palabra clave dentro de la lambda. Ahora le hemos ordenado al compilador que regrese de la lambda en lugar de la función que contiene alrededoresFunción (). Ahora la última declaración de alrededoresFunción () ejecutará. 

Tenga en cuenta que también podemos definir nuestra propia etiqueta o etiqueta de nombre. 

 // ... intList.forEach myLabel @ if (it% 2 == 0) return @ myLabel // ... 

En el código anterior, definimos nuestra etiqueta personalizada llamada myLabel @ y luego lo especificó para el regreso palabra clave. los @para cada Etiqueta generada por el compilador para el para cada La función ya no está disponible porque hemos definido nuestro propio. 

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

3. Funciones de los miembros

Este tipo de función se define dentro de una clase, objeto o interfaz. El uso de funciones miembro nos ayuda a modularizar nuestros programas aún más. Veamos ahora cómo crear una función miembro..

class Circle fun calculaArea (radio: Double): Double require (radio> 0, "El radio debe ser mayor que 0") return Math.PI * Math.pow (radio, 2.0)

Este fragmento de código muestra una clase Circulo (discutiremos las clases de Kotlin en publicaciones posteriores) que tiene una función miembro calcularArea (). Esta función toma un parámetro. radio para calcular el área de un círculo.

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

val circle = Circle () print (circle.calculateArea (4.5)) // imprimirá "63.61725123519331"

4. Funciones anónimas

Una función anónima es otra forma de definir un bloque de código que se puede pasar a una función. No está vinculado a ningún identificador. Aquí están las características de una función anónima en Kotlin:

  • no tiene nombre
  • se crea con el divertido palabra clave
  • contiene un cuerpo de función
val stringList: lista = listOf ("in", "the", "club") print (stringList.last it.length == 3) // imprimirá "the"

Porque pasamos una lambda a la último() función arriba, no podemos ser explícitos sobre el tipo de retorno. Para ser explícitos sobre el tipo de retorno, necesitamos usar una función anónima en su lugar.

val strLenThree = stringList.last (fun (string): Boolean return string.length == 3) print (strLenThree) // imprimirá "the"

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

Hacia el final de la sección lambda en este tutorial, usamos una etiqueta para especificar desde qué función regresar. Usando una función anónima en lugar de una lambda dentro de la para cada() La función resuelve este problema más simplemente. La expresión de retorno regresa de la función anónima y no de la que la rodea, que en nuestro caso es alrededoresFunción ().

fun surroundingFunction () val intList = listOf (1, 2, 3, 4, 5) intList.forEach (fun (number) if (number% 2 == 0) return) println ("End of surroundingFunction ( ) ") // sentencia ejecutada surroundingFunction () // imprimirá" Fin de surroundingFunction () "

5. Funciones locales o anidadas

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

fun printCircumferenceAndArea (radio: Double): Unit fun calCircumference (radio: Double): Double = (2 * Math.PI) * radius val circumference = "% .2f" .format (calCircumference (radio)) fun calArea (radio: Doble): Double = (Math.PI) * Math.pow (radio, 2.0) val area = "% .2f" .format (calArea (radio)) print ("La circunferencia del círculo del radio de $ es la circunferencia y el área is $ area ") printCircumferenceAndArea (3.0) // La circunferencia del círculo de 3.0 radio es 18.85 y el área es 28.27

Como puede observar en el fragmento de código anterior, tenemos dos funciones de una sola línea: calcircunferencia () y calArea () anidado dentro del printCircumferenceAndAread () función. Las funciones anidadas solo se pueden llamar desde dentro de la función de cierre y no desde fuera. De nuevo, el uso de funciones anidadas hace que nuestro programa sea más modular y ordenado.. 

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

fun printCircumferenceAndArea (radio: Double): Unit fun calCircumference (): Double = (2 * Math.PI) * radio val circumference = "% .2f" .format (calCircumference ()) fun calArea (): Double = (Math .PI) * Math.pow (radio, 2.0) área val = "% .2f" .format (calArea ()) //…

Como puede ver, este código actualizado parece más legible y reduce el ruido que teníamos antes. Aunque la función de encierre en este ejemplo dado es pequeña, en una función de encierro más grande que se puede dividir en funciones anidadas más pequeñas, esta característica realmente puede ser útil. 

6. Funciones de Infix

los infijo La notación nos permite llamar fácilmente a una función miembro de un argumento o una función de extensión. Además de que una función es un argumento, también debe definir la función utilizando el infijo modificador Para crear una función de infijo, se involucran dos parámetros. El primer parámetro es el objeto de destino, mientras que el segundo parámetro es solo un único parámetro pasado a la función. 

Creando una Función Miembro Infix

Veamos cómo crear una función de infijo en una clase. En el siguiente ejemplo de código, creamos un Estudiante clase con un mutable kotlinScore campo de instancia Creamos una función de infijo usando el infijo modificador antes de la divertido palabra clave. Como se puede ver a continuación, creamos una función de infijo. addKotlinScore () que toma una puntuación y se suma a la kotlinScore campo de instancia. 

clase Estudiante var kotlinScore = 0.0 infijo divertido addKotlinScore (puntaje: Doble): Unidad this.kotlinScore = kotlinScore + puntaje

Llamando a una función de infijo

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

val student = Student () student addKotlinScore 95.00 print (student.kotlinScore) // imprimirá "95.0"

En el código anterior, llamamos la función infijo, el objeto de destino es estudiante, y el doble 95.00 es el parámetro pasado a la función. 

El uso inteligente de las funciones de infijo puede hacer que nuestro código sea más expresivo y claro que el estilo normal. Esto es muy apreciado al escribir pruebas unitarias en Kotlin (discutiremos las pruebas en Kotlin en una publicación futura).

"Chike" debería comenzarCon ("ch") myList debería contener (myElement) "Chike" debería tenerLength (5) myMap debería tenerKey (myKey) 

los a Función Infix

En Kotlin, podemos hacer la creación de un Par instancia más sucinta mediante el uso de la a función de infijo en lugar de la Par constructor. (Entre bastidores, a también crea un Par instancia.) Tenga en cuenta que la a La función también es una función de extensión (veremos esto más en la próxima publicación).

infix público divertido  A.to (que: B): Par = Par (esto, eso)

Comparemos ahora la creación de un Par instancia utilizando tanto la a función de infijo y directamente utilizando el Par constructor, que realiza la misma operación, y ve cuál es mejor.

val nigeriaCallingCodePair = 234 a "Nigeria" val nigeriaCallingCodePair2 = Par (234, "Nigeria") // Igual que el anterior

Como puede ver en el código anterior, usando el a La función de infijo es más concisa que directamente usando el Par constructor para crear una Par ejemplo. Recuerda que usando el a función de infijo, 234 es el objeto objetivo y el Cuerda "Nigeria" es el parámetro pasado a la función. Además, tenga en cuenta que también podemos hacer esto para crear un Par tipo:

val nigeriaCallingCodePair3 = 234.to ("Nigeria") // igual que usar 234 para "Nigeria"

En la publicación 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. Comparemos también la creación de un mapa usando tanto a función de infijo y la Par constructor para crear los pares individuales.

val callingCodesMap: Mapa = mapOf (234 a "Nigeria", 1 a "USA", 233 a "Ghana")

En el código anterior, creamos una lista separada por comas de Par tipos usando el a Función de infijo y los pasó a la mapa de() función. También podemos crear el mismo mapa usando directamente Par constructor para cada par.

val callingCodesPairMap: Mapa = mapOf (Par (234, "Nigeria"), Par (1, "USA"), Par (233, "Ghana"))

Como puedes ver de nuevo, pegándote a la a La función de infijo tiene menos ruido que usar el Par constructor. 

Conclusión

En este tutorial, aprendiste sobre algunas de las cosas geniales que puedes hacer con las funciones en Kotlin. Cubrimos

  • funciones de nivel superior
  • Expresiones lambda o funciones literales.
  • funciones miembro
  • funciones anonimas
  • funciones locales o anidadas
  • funciones de infijo

¡Pero eso no es todo! Todavía hay más que aprender acerca de las funciones en Kotlin. Por lo tanto, en la próxima publicación, aprenderá algunos usos avanzados de las funciones, como funciones de extensión, funciones de orden superior y cierres. Te veo pronto!

Para obtener más información sobre el idioma Kotlin, recomiendo visitar la documentación de Kotlin. O echa un vistazo a algunas de nuestras otras publicaciones de desarrollo de aplicaciones para Android aquí en Envato Tuts+!