() translation by (you can also view the original English article)
Kotlin es un moderno lenguaje de programación que compila a código byte Java. Es gratuito y de código abierto, y promete hacer el código para Android incluso más divertido.
En el artículo anterior, aprendiste sobre rangos y colecciones en Kotlin. En este tutorial, continuaremos aprendiendo el lenguaje viendo cómo organizar código usando paquetes, y después continuaremos a una introducción a funciones en Kotlin.
1. Paquetes
Si estás familiarizado con Java, sabes que Java usa paquetes para agrupar clases relacionadas; por ejemplo, el paquete java.util
tiene un número de útiles clases utilitarias. Los paquetes son declarados con la palabra clave package
, y cualquier archivo Kotlin con una declaración package
al inicio puede contener declaraciones de clases, funciones o interfaces.
Declaración
Viendo el código de abajo, hemos declarado un paquete com.chikekotlin.projectx
usando la palabra clave package
. También, declaramos una clase MyClass
(discutiremos clases en Kotlin en artículos futuros) dentro de este paquete.
1 |
package com.chikekotlin.projectx |
2 |
|
3 |
class MyClass |
Ahora, el nombre completamente calificado para la clase MyClass
es com.chikekotlin.projectx.MyClass
.
1 |
package com.chikekotlin.projectx |
2 |
|
3 |
fun saySomething(): String { |
4 |
return "How far?" |
5 |
}
|
En el código de arriba, creamos una función de nivel superior (llegaremos a eso pronto). Así que de manera similar a MyClass
, el nombre completamente calificado para la función saySomething()
es com.chikekotlin.projectx.saySomething
.
Importaciones
En Kotlin, usamos la declaración import
para habilitar al compilador para localizar las clases, funciones, interfaces u objetos a ser importados. En Java, por el otro lado, no podemos importar directamente funciones o métodos---solo clases o interfaces.
Usamos import
para acceder a una función, interfaz, clase u objeto fuera del paquete en donde fue declarado.
1 |
import com.chikekotlin.projectx.saySomething |
2 |
|
3 |
fun main(args: Array<String>) { |
4 |
saySomething() // will print "How far?" |
5 |
}
|
En el pedazo de código de arriba, importamos la función saySomething()
desde un paquete diferente, y después ejecutamos esa función.
Kotlin también soporta importaciones comodín usando el operador *
. Eso importará todas las clases interfaces y funciones declaradas en el paquete a la vez. Esto no es recomendado, sin embargo---es mejor usualmente hacer tus importaciones explícitas.
1 |
import com.chikekotlin.projectx.* |
Importar Aliasing
Cuando tienes librerías que tienen nombres de clase o función en conflicto (ej. cada una declara una función con el mismo nombre), puedes usar la palabra clave as
para darle a esa entidad importada un nombre temporal.
1 |
import com.chikekotlin.projectx.saySomething |
2 |
import com.chikekotlin.projecty.saySomething as projectYSaySomething |
3 |
|
4 |
fun main(args: Array<String>) { |
5 |
projectYSaySomething() |
6 |
}
|
Nota que el nombre temporal es usado solo dentro del archivo en donde fue asignado.
2. Funciones
Una función agrupa una serie de declaraciones de código que realizan una tarea. Los detalles de la implementación de la función están ocultos del llamador.
En Kotlin, las funciones están definidas usando la palabra clave fun
, como se muestra en el siguiente ejemplo:
1 |
fun hello(name: String): String { |
2 |
return "Hello $name" |
3 |
}
|
4 |
val message = hello("Chike") |
5 |
print(message) // will print "Hello Chike" |
En el código de arriba, definimos una función simple hello()
con un solo parámetro name
del tipo String
. Esta función devuelve un tipo String
. El formato de definición de parámetros para funciones es: nombre: tipo
, ej. edad: Int
, precio: Double
, estudiante: StudentClass
.
1 |
fun hello(name: String): Unit { |
2 |
print("Hello $name") |
3 |
}
|
4 |
|
5 |
hello("Chike") // will print "Hello Chike" |
La función de arriba es similar a la anterior, pero nota que esta tiene un tipo de retorno de Unit
. Debido a que esta función no nos devuelve ningún valor significativo---solo imprime un mensaje---su tipo de retorno es Unit
por defecto. Unit
es un objeto Kotlin (discutiremos objetos Kotlin en artículos posteriores) que es similar a los tipos Void
en Java y C.
1 |
public object Unit { |
2 |
override fun toString() = "kotlin.Unit" |
3 |
}
|
Nota que si no declaras explícitamente el tipo de retorno para ser Unit
, el tipo es inferido por el compilador.
1 |
fun hello(name: String) { // will still compile |
2 |
print("Hello $name") |
3 |
}
|
Funciones de Una Sola Línea
Las funciones de una sola línea son funciones que son expresiones de una sola línea. En esta función, nos deshacemos de los corchetes y usamos el símbolo =
antes de la expresión. En otras palabras, nos deshacemos de la función bloque.
1 |
fun calCircumference(radius: Double): Double { |
2 |
return (2 * Math.PI) * radius |
3 |
}
|
La función de arriba puede ser acortada en una sola línea:
1 |
fun calCircumference(radius: Double) = (2 * Math.PI) * radius |
Mirando a la función actualizada de arriba, puedes ver que hemos hecho nuestro código más conciso removiendo las llaves {}
, la palabra clave return
, y también el tipo de retorno (que es inferido por el compilador).
Aún puedes incluir el tipo de retorno para ser más explícito si quieres.
1 |
fun calCircumference(radius: Double): Double = (2 * Math.PI) * radius |
Parámetros Nombrados
Los parámetros nombrados permiten funciones más legibles nombrando los parámetros que están siendo pasados a una función cuando se llaman.
En el siguiente ejemplo, creamos una función que imprime mi nombre completo.
1 |
fun sayMyFullName(firstName: String, lastName: String, middleName: String): Unit { |
2 |
print("My full name is $firstName $middleName $lastName"); |
3 |
}
|
Para ejecutar la función de arriba, solo la llamaríamos así:
1 |
sayMyFullName("Chike", "Nnamdi", "Mgbemena") |
Viendo a la llamada de función de arriba, no sabemos cuáles tipos de argumentos String
son iguales a cuáles parámetros de función (aunque algunos IDEs como IntelliJ IDEA nos pueden ayudar). Los usuarios de la función tendrán que buscar en la firma de la función (o el código fuente) o documentación para saber a qué corresponde cada parámetro.
1 |
sayMyFullName(firstName = "Chike", middleName = "Nnamdi", lastName = "Mgbemena") |
En la segunda llamada de función de arriba, proporcionamos los nombres de parámetro antes de los valores de argumento. Puedes ver que esta llamada de función es más clara y más legible que la anterior. Esta manera de llamar funciones ayuda a reducir la posibilidad de errores que pueden ocurrir cuando los argumentos del mismo tipo son intercambiados por error.
El llamador también puede alterar el orden de los parámetros usando parámetros nombrados. Por ejemplo:
1 |
sayMyFullName(lastName = "Mgbemena", middleName = "Nnamdi", firstName = "Chike") // will still compile |
En el código de arriba, intercambiamos la posición de argumento de firstName
con lastName
. El orden del argumento no importa con parámetros nombrados porque el compilador mapeará cada uno de ellos al parámetro correcto de función.
Parámetros Por Defecto
En Kotlin, podemos darle a una función valores por defecto para cualquiera de sus parámetros. Estos valores por defecto se usan si nada es asignado a los argumentos durante la llamada de la función. Para hacer esto en Java, necesitaríamos crear diferentes métodos sobrecargados.
Aquí, en nuestro método calCircumference()
, modificamos el método agregando un valor por defecto para el parámetro pi
---Math.PI
, una constante del paquete java.lang.Math
.
1 |
fun calCircumference(radius: Double, pi: Double = Math.PI): Double = (2 * pi) * radius |
Cuando llamamos esta función, podemos pasar nuestro valor aproximado para pi
o usar el valor por defecto.
1 |
print(calCircumference(24.0)) // used default value for PI and prints 150.79644737231007 |
2 |
print(calCircumference(24.0, 3.14)) // passed value for PI and prints 150.72 |
Veamos otro ejemplo.
1 |
fun printName(firstName: String, middleName: String = "N/A", lastName: String) { |
2 |
println("first name: $firstName - middle name: $middleName - last name: $lastName") |
3 |
}
|
En el siguiente código, intentamos llamar a la función, pero no compilará:
1 |
printName("Chike", "Mgbemena") // won't compile |
En la llamada de función de arriba, estoy pasando mi primer nombre y apellido a la función, y esperando usar el valor por defecto para el nombre medio. Pero esto no compilará porque el compilador está confundido. Este no sabe para qué es el argumento "Mgbemena"---¿es para el parámetro middleName
o para lastName
?
Para resolver este asunto, podemos combinar parámetros nombrados y parámetros por defecto.
1 |
printName("Chike", lastName = "Mgbemena") // will now compile |
Interoperabilidad Java
Dado que Java no soporta valores de parámetro por defecto en métodos, necesitarás especificar todos los valores de parámetro de manera explícita cuando llamas una función Kotlin desde Java. Pero Kotlin nos proporciona la funcionalidad para hacer más sencillo para los llamadores Java anotando la función Kotlin con @JvmOverloads
. Esta anotación instruirá al compilador Kotlin para generar las funciones sobrecargadas Java por nosotros.
En el siguiente ejemplo, anotamos la función calCirumference()
con @JvmOverloads
.
1 |
@JvmOverloads
|
2 |
fun calCircumference(radius: Double, pi: Double = Math.PI): Double = (2 * pi) * radius |
El siguiente código fue generado por el compilador Kotlin para que los llamadores Java puedan entonces elegir a cuál llamar.
1 |
// Java
|
2 |
double calCircumference(double radius, double pi); |
3 |
double calCircumference(double radius); |
En la última definición de método Java generada, el parámetro pi
fue omitido. Esto significa que el método usará el valor pi
por defecto.
Argumentos Ilimitados
En Java, podemos crear un método para recibir un número no especificado de argumentos incluyendo puntos suspensivos (...
) después de un tipo en la lista de parámetro del método. Esto concepto también es soportado por funciones Kotlin con el uso del modificador vararg
seguido por el nombre del parámetro.
1 |
fun printInts(vararg ints: Int): Unit { |
2 |
for (n in ints) { |
3 |
print("$n\t") |
4 |
}
|
5 |
}
|
6 |
printInts(1, 2, 3, 4, 5, 6) // will print 1 2 3 4 5 6 |
El modificador vararg
permite a los llamadores pasar una lista separada por comas de argumentos. Tras bambalinas, esta lista de argumentos será envuelta en un arreglo.
Cuando una función tiene múltiples parámetros, el parámetro vararg
es típicamente el último. También es posible tener parámetros después de vararg
, pero necesitarás usar parámetros nombrados para especificarlos cuando llamas a la función.
1 |
fun printNumbers(myDouble: Double, myFloat: Float, vararg ints: Int) { |
2 |
println(myDouble) |
3 |
println(myFloat) |
4 |
for (n in ints) { |
5 |
print("$n\t") |
6 |
}
|
7 |
}
|
8 |
printNumbers(1.34, 4.4F, 2, 3, 4, 5, 6) // will compile |
Por ejemplo, en el código de arriba, el parámetro con el modificador vararg
está en la última posición en una lista de múltiples parámetros (esto es lo que hacemos usualmente). ¿Pero qué si no lo queremos en la última posición? En el siguiente ejemplo, está en la segunda posición.
1 |
fun printNumbers(myDouble: Double, vararg ints: Int, myFloat: Float) { |
2 |
println(myDouble) |
3 |
println(myFloat) |
4 |
for (n in ints) { |
5 |
print("$n\t") |
6 |
}
|
7 |
}
|
8 |
printNumbers(1.34, 2, 3, 4, 5, 6, myFloat = 4.4F) // will compile |
9 |
printNumbers(1.34, ints = 2, 3, 4, 5, 6, myFloat = 4.4F) // will not compile |
10 |
printNumbers(myDouble = 1.34, ints = 2, 3, 4, 5, 6, myFloat = 4.4F) // will also not |
11 |
compile
|
Como puedes observar en el código actualizado de arriba, usamos argumentos nombrados en el último parámetro para solucionar esto.
Operador Propagado
Digamos que queremos pasar un arreglo de enteros a nuestra función printNumbers()
. La función espera que los valores sean desenrrollados a una lista de parámetros, sin embargo. Si intentas pasar el arreglo directamente a printNumbers()
, verás que no compilará.
1 |
val intsArray: IntArray = intArrayOf(1, 3, 4, 5) |
2 |
printNumbers(1.34, intsArray, myFloat = 4.4F) // won't compile |
Para resolver este problema, necesitamos usar el operador *
. Este operador desempacará el arreglo y después pasará los elementos individuales como argumentos a la función por nosotros.
1 |
val intsArray: IntArray = intArrayOf(1, 3, 4, 5) |
2 |
printNumbers(1.34, *intsArray, myFloat = 4.4F) // will now compile |
Insertando el operador *
en frente de instArray
en la lista de argumentos de la función, el código ahora compila y produce el mismo resultado que si hubiéramos pasado los elementos de intsArray
como una lista separada por comas de argumentos.
Devuelve Múltiples Valores
Algunas veces queremos devolver múltiples valores desde una función. Una manera es usar el tipo Pair
en Kotlin para crear un Pair
y después devolverlo. Esta estructura Pair
encierra dos valores que después pueden ser valorados. Este tipo Kotlin puede aceptar cualquier tipo que suministres a su constructor. Y, lo que es más, los dos tipos ni siquiera tienen que ser el mismo.
1 |
fun getUserNameAndState(id: Int): Pair<String?, String?> { |
2 |
require(id > 0, { "Error: id is less than 0" }) |
3 |
|
4 |
val userNames: Map<Int, String> = mapOf(101 to "Chike", 102 to "Segun", 104 to "Jane") |
5 |
val userStates: Map<Int, String> = mapOf(101 to "Lagos", 102 to "Imo", 104 to "Enugu") |
6 |
|
7 |
val userName = userNames[id] |
8 |
val userState = userStates[id] |
9 |
return Pair(userName, userState) |
10 |
}
|
En la función de arriba, construimos un nuevo Pair
pasando las variables userName
y userState
como el primer y segundo argumento respectivamente a su constructor, y después devolvimos su Pair
al llamador.
Otra cosa que notar es que usamos una función llamada require()
en la función getUserNameAndState()
. Esta función de ayuda desde la librería estándar es usada para dar nuestros llamadores de función una condición previa para cumplir, o de otro modo IllegalArgumentException
será lanzado (discutiremos Excepciones en Kotlin en un artículo futuro). El segundo argumento opcional para require()
es una función literal devolviendo un mensaje para ser mostrado si la excepción es lanzada. Por ejemplo, llamar a la función getUserNameAndState()
y pasar -1
como un argumento disparará:



Recuperando Datos Desde Pair
1 |
val userNameAndStatePair: Pair<String?, String?> = getUserNameAndState(101) |
2 |
println(userNameAndStatePair.first) // Chike |
3 |
println(userNameAndStatePair.second) // Lagos |
En el código de arriba, accedimos el primer y segundo valores desde el tipo Pair
usando sus propiedades first
y second
.
Sin embargo, hay una mejor forma de hacer esto: desestructurando.
1 |
val (name, state) = getUserNameAndState(101) |
2 |
println(name) // Chike |
3 |
println(state) // Lagos |
Lo que hemos hecho en el código actualizado de arriba es asignar directamente el primer y segundo valor del tipo Pair
devuelto a las variables name
y state
respectivamente. Esta característica es llamada declaración de desestructuración.
Valores Triples de Retorno y Más allá
Ahora, ¿qué si quieres devolver tres valores a la vez? Kotlin nos proporciona con otro tipo útil llamada Triple
.
1 |
fun getUserNameStateAndAge(id: Int): Triple<String?, String?, Int> { |
2 |
require(id > 0, { "id is less than 0" }) |
3 |
|
4 |
val userNames: Map<Int, String> = mapOf(101 to "Chike", 102 to "Segun", 104 to "Jane") |
5 |
val userStates: Map<Int, String> = mapOf(101 to "Lagos", 102 to "Imo", 104 to "Enugu") |
6 |
|
7 |
val userName = userNames[id] |
8 |
val userState = userStates[id] |
9 |
val userAge = 6 |
10 |
return Triple(userNames[id], userStates[id], userAge) |
11 |
}
|
12 |
|
13 |
val (name, state, age) = getUserNameStateAndAge(101) |
14 |
println(name) // Chike |
15 |
println(state) // Lagos |
16 |
println(age) // 6 |
Estoy seguro de que algunos de ustedes se están preguntando qué hacer si quieres devolver más de tres valores. La respuesta para eso estará en un artículo futuro, cuando discutamos las clases de datos de Kotlin.
Conclusión
En este tutorial, aprendiste sobre los paquetes y funciones básicas en el lenguaje de programación Kotlin. En el siguiente tutorial en la serie Kotlin Desde Cero, aprenderás más sobre funciones en Kotlin. ¡Nos vemos pronto!
Para aprender más sobre el lenguaje Kotlin, recomiendo visitar la documentación Kotlin. ¡O revisa algunos de nuestros otros artículos de desarrollo de apps Android aquí en Envato Tuts+!
- Android SDKIntroducción a Componentes de Arquitectura AndroidTin Megali
- Machine LearningCrea Chatbots en Android Con IBM WatsonAshraff Hathibelagal
- Android SDKJava vs. Kotlin: ¿Deberías Estar Usando Kotlin para Desarrollo Android?Jessica Thornsby
- Android SDKConsejo Rápido: Escribe Código Más Limpio con Conversiones SAM KotlinAshraff Hathibelagal
- Android SDKAndroid O: Verificación de Número de Teléfono Con Tokens SMSChike Mgbemena