Advertisement
  1. Code
  2. Kotlin

Kotlin Desde Cero: Paquetes y Funciones Básicas

Scroll to top
Read Time: 13 min
This post is part of a series called Kotlin From Scratch.
Kotlin From Scratch: Ranges and Collections
Kotlin From Scratch: More Fun With Functions

() 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á:

IntelliJ IDEA code execution resultIntelliJ IDEA code execution resultIntelliJ IDEA code execution result

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+!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
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.