La clase ListaLugares

Ejercicio: La clase LugaresLista

En este ejercicio vamos a crear la clase LugaresLista que tiene como finalidad almacenar y gestionar un conjunto de objetos Lugar dentro de una lista.

1.    Dentro del paquete  org.example.mislugares añade la clase LugaresLista,  y  reemplaza el código por el siguiente:
 

public class LugaresLista implements RepositorioLugares {
    protected List listaLugares;

    public LugaresLista() {
        listaLugares = new ArrayList<>();
      añadeEjemplos();

    }

    public Lugar elemento(int id) {
        return listaLugares.get(id);
    }

    public void añade(Lugar lugar) {
        listaLugares.add(lugar);
    }

    public int nuevo() {
        Lugar lugar = new Lugar();
        listaLugares.add(lugar);
        return listaLugares.size()-1;
    }

    public void borrar(int id) {
        listaLugares.remove(id);
    }

    public int tamaño() {
        return listaLugares.size();
    }
    public void actualiza(int id, Lugar lugar) {
        listaLugares.set(id, lugar);
    }

    public void añadeEjemplos() {
    añade(new Lugar("Escuela Politécnica Superior de Gandía",
            "C/ Paranimf, 1 46730 Gandia (SPAIN)", -0.166093, 38.995656,
            TipoLugar.EDUCACION, 962849300, "http://www.epsg.upv.es",
            "Uno de los mejores lugares para formarse.", 3));
    añade(new Lugar("Al de siempre",
            "P.Industrial Junto Molí Nou - 46722, Benifla (Valencia)",
            -0.190642, 38.925857, TipoLugar.BAR, 636472405, "",
            "No te pierdas el arroz en calabaza.", 3));
    añade(new Lugar("androidcurso.com",
            "ciberespacio", 0.0, 0.0, TipoLugar.EDUCACION,
            962849300, "http://androidcurso.com",
            "Amplia tus conocimientos sobre Android.", 5));
    añade(new Lugar("Barranco del Infierno",
            "Vía Verde del río Serpis. Villalonga (Valencia)",
            -0.295058, 38.867180, TipoLugar.NATURALEZA, 0,
            "http://sosegaos.blogspot.com.es/2009/02/lorcha-villalonga-via-"+
                    "verde-del-rio.html","Espectacular ruta para bici o andar", 4));
    añade(new Lugar("La Vital",
            "Avda. de La Vital, 0 46701 Gandía (Valencia)", -0.1720092,
            38.9705949, TipoLugar.COMPRAS, 962881070,
            "http://www.lavital.es/", "El típico centro comercial", 2));

   }
}
class LugaresLista : RepositorioLugares {
   val listaLugares= mutableListOf<Lugar>()

   override fun elemento(id: Int): Lugar {
      return listaLugares[id]
   }

   override fun añade(lugar: Lugar) {
      listaLugares.add(lugar)
   }

   override fun nuevo(): Int {
      val lugar = Lugar("Nuevo lugar")
      listaLugares.add(lugar)
      return listaLugares.size - 1
   }

   override fun borrar(id: Int) {
      listaLugares.removeAt(id)
   }

   override fun tamaño(): Int {
      return listaLugares.size
   }

   override fun actualiza(id: Int, lugar: Lugar) {
      listaLugares[id] = lugar
   }
} 

2.    Pulsa Alt-Intro e para que se añadan los import de las clases utilizadas.

import java.util.ArrayList;
import java.util.List;

3.    Para Java Añade la siguiente sobrecarga al constructor a la clase Lugar:

public Lugar() {
   fecha = System.currentTimeMillis();
   posicion =  GeoPunto.(0.0, 0.0);
   tipo = TipoLugar.OTROS;
}

Esto nos permitirá crear un nuevo lugar sin indicar sus atributos.

4.    Abre la clase  Principal y reemplaza el código del método main() por:

RepositorioLugares lugares = new LugaresLista();
for (int i=0; i<lugares.tamaño(); i++) {
    System.out.println(lugares.elemento(i).toString());
}

5.    Verifica que el resultado es similar al siguiente:

Sobre el curso de Kotlin

Kotlin ha sido nombrado, junto con Java, lenguaje oficial para el desarrollo en Android. Presenta características muy interesantes, entre las que podemos destacar: es conciso, expresivo y potente. Además, es interoperable con Java, por lo que podemos utilizar todas las librerías ya disponibles y la migración de una aplicación que ya tenemos escrita en Java puede realizarse por partes.
Kotlin está perfectamente integrado en Android Studio, todas las herramientas de optimización de código, etc., están disponibles para este lenguaje. Disponemos de herramientas para traducir código de Java a Kotlin. 
A lo largo de los siguientes artículos describiremos las principales características de Kotlin, centrándonos en aquellas que lo diferencian de Java. Se han organizado en los siguientes puntos: estructuras de control, variables, funciones, clases, Lambdas, Anko, Tratamiento de null, etc.  
 

Objetivos:

  • Resaltar las características más interesantes de Kotlin que lo convierten en una alternativa atractiva frente a Java..
  • Mostrar cómo podemos utilizar estructuras de control como for y when.
  • Aprender a declarar variables y funciones.
  • Conocer nuevas características de las clases, como clases de datos y extensiones.
  • Utilizar Lambdas para parametrizar funciones.
  • Enumerar las facilidades que nos aporta la librería Anko para el desarrollo en Android.
  • Evitar errores con variables sin inicializar gracias a nuevas características introducidas en el lenguaje.
  • Comparar las clases selladas y enumeradas.
  • Conocer las nuevas clases para colecciones.

Inicialización de propiedades: lateinit vs lazy

video[Tutorial Inicialización de propiedades lateinit vs lazy

Normalmente, aquellas variables que no se declaren como nulas, se deben inicializar en el constructor. Sin embargo, frecuentemente esto no es lo más adecuado. En caso de que no queramos inicializar una propiedad en el constructor, disponemos de dos alternativas, lateinit y lazy.
Mediante lateinit lo que conseguimos es una inicialización tardía, indicando al compilador que dicha inicialización se realizará más adelante en el código. Si intentamos acceder a alguna variable antes de su inicialización obtendremos un error del tipo Caused by: kotlin.UninitializedPropertyAccessException: lateinit property test has not been initialized.
 

lateinit var texto: String
fun doSomething() {
   texto = "Un valor"
   println("La longitud es "+texto.length)
   texto = "Otro valor"
} 
El modificador lateinit solo puede ser utilizado en variables mutables (var) declaradas en el cuerpo de una clase (no el en constructor primario), y solo cuando dicha variable no tiene un getter o setter personalizado.
Mediante lazy lo que conseguimos es una inicialización perezosa, con lo que una variable no será inicializada a no ser que sea utilizada en nuestro código. Será inicializada una única vez tras lo cual mantendrá el valor.
public class Ejemplo{
   val nombre: String by lazy { "Gonzalo Puga" }
} 
En este ejemplo, tanto las primeras llamadas a nombre como las subsecuentes, devolverán "Gonzalo Puga"
Una vez que sabemos qué hace cada una de estas funciones, ¿cuándo debemos utilizar cada una de ellas?
  • *   lazy solo se puede utilizar para variables inmutables val, mientras que lateinit solo se pueden aplicar a variables mutables var, ya que no se puede asegurar la inmutabilidad de dicha variable al no poder compilarse como final.
  • *   Una variable de tipo lateinit se podrá inicializar en cualquier parte del objeto donde sea visible. Si necesitamos que nuestra variable sea inicializada desde fuera, de un modo desconocido de antemano, deberemos utilizar lateinit.

Colecciones en Kotlin: List, Set y Map

video[Tutorial Colecciones en Kotlin: List, Set y Map

Una lista es una colección ordenada de elementos. Para trabajar con listas inmutables se utiliza la interface List. Extiende de la interfaz Collection, y define una serie de funciones propias, entre las que podemos destacar:

  •    get(index: Int): elemento almacenado en el índice especificado.
  •    indexOf(element: E): índice de la primera ocurrencia del elemento pasado como un argumento en la lista, o -1 si ninguno es encontrado.
  •    lastIndexOf(element: E): índice de la última ocurrencia del elemento pasado como un argumento en la lista, o -1 si ninguno es encontrado.
  •    listIterator(): iterador sobre los elementos en la lista.
  •    subList(fromIndex: Int, toIndex: Int): lista que contiene la porción de la lista entre los índices especificados de inicio y fin.

En Kotlin tenemos diversos modos de crear listas:

  •     listOf() permite crear una lista inmutable. Podremos tener todos los elementos de un mismo tipo o de diferentes tipos de datos:
val primos: List<Int> = listOf(2, 3, 5, 7)
val nombres: List<String> = listOf("Juan", "Roberto", "María")
val listaMezclada = listOf("Juan", 1, 2.445, 's') 
  •    emptyList() crea una lista inmutable vacía:
val listaVacia: List<String> = emptyList<String>() 
  •    listOfNotNull() crea una lista inmutable con solo elementos no nulos:
val listaNoNulos: List<Int> = listOfNotNull(2, 45, 2, null, 5, null) 
  •    arrayListOf() crea una lista inmutable de tipo ArrayList:
val arrayList: ArrayList<String> = arrayLis-tOf<String>("un", "dos", "tres)   
Para trabajar con listas mutables utiliza un desdendiente de MutableList. Esta interfaz extiende las interfaces MutableCollection y List discutidas anteriormen-te en esta sección. La interfaz MutableList agrega métodos para la recuperación o sustitución de un elemento basado en su posición:
  •    set(index: Int, element: E): sustituye un elemento en la lista, devolviendo el elemento que estaba previamente en la posición especificada.
  •    add(index: Int, element: E): inserta un elemento.
  •    removeAt(index: Int): elimina el elemento en un índice particular.
Para convertir una lista inmutable en mutable utiliza la función toMutableList(). Este método creará una nueva lista:
Para crear una lista mutable de un cierto tipo desde cero, por ejemplo, de String, usamos mutableListOf&lt;String&gt;(), mientras que para tipos mixtos podemos solo usar la función mutableListOf() en su lugar:
val listaMutable: MutableList<String> = mutableLis-tOf<String>("uno","dos")
listaMutable.add("tres")
listaMutable.removeAt(1)
listaMutable[0] = "cuatro" 
val mutableListMixed = mutableListOf("BMW", "Toyota", 1, 6.76, 'v')  

Conjuntos

Un conjunto (Set) es una colección sin orden de elementos únicos, esto es, no puede tener ningún duplicado. Set extiende de la interfaz Collection, por lo que es inmutable, y no agrega nuevos métodos. Para crear conjuntos inmutables utiliza:
  •    setOf() crea un conjunto inmutable y devuelve un objeto de tipo Set.
val conjunto: Set<Int> = setOf(1, 3, 4)
val conjuntoMezclado = setOf(2, 4.454, "casa", 'c')  

Mapas

Los mapas asocian claves con valores. Las claves deben ser únicas, pero los valores asociados no. De este modo, cada valor puede ser usado para identificar de manera única el valor asociado, ya que el mapa asegura que no puedes duplicar claves en la colección. Internamente, Kotlin usa la colección Java Map para implementar los mapas.
A diferencia de las interfaces List y Set en Kotlin que extienden la interfaz Collection, la interfaz Map no extiende nada. Algunas de las propiedades y funciones disponibles en esta interfaz se muestran a [g1] continuación. Observa como solo se permite hacer consultas, al definir una colección inmutable.  
  •    size: tamaño de la colección.
  •    isEmpty(): indica si el mapa está vacío.
  •    containsKey(key: K): indica si el mapa contiene una clave.
  •    containsValue(value: V): indica si el mapa contiene un valor.
  •    get(key: K): valor asociado a la llave dada o null si no se encuentra.
  •    keys: devuelve un Set inmutable con todas las claves en el mapa.
  •    values: Collection inmutable de todos los valores en el mapa.
mapOf() crea un mapa inmutable compuesto por una lista de pares, donde el primer valor es la clave, y el segundo es el valor. Devuelve un objeto de tipo Map.
val prefijos: Map<Int, String> = mapOf(34 to "España", 1 to "USA", 
                                                           233 to "Ghana")
for ((key, value) in prefijos) {
    println("$key es el código telefónico de $value")
}  
Podemos obtener el valor de una clave usando la función get(). También podemos usar los corchetes como un atajo para get().
print(prefijos.get(34)) // España
print(prefijos[34])     // España 
La interfaz MutableMap no extiende la interfaz MutableCollection; su único padre es la interfaz Map. Este anula las propiedades keys, entries y values de la interfaz padre para poder redefinirlas. Además, incluye algunas funciones extra como:
  •    put(key: K, value: V) inserta el par clave-valor en el mapa. Devolverá el valor previo enlazado con la clave o null si la clave no existía.
  •    remove(key: K) borra la clave y su valor enlazado.
  •    putAll(from: Map&lt;out K, V&gt;)  agrega nuevos pares clave-valor desde otro mapa. Si una clave ya existente será actualizada con el nuevo valor.
  •    clear() elimina todos los elementos del mapa.
mutableMapOf() permite crear un mapa mutable sin indicar la implementación:
val monedas: MutableMap<String, String> = mutableMapOf("euro" to "España", 
                                  "dolar" to "EEUU", "libra" to "UK")
println("Paises ${ monedas.values}") 
println("Monedas ${ monedas.keys}") 
monedas.put("cedi", "Ghana")
monedas.remove("dolar")  
Para indicar implementaciones específicas dispones de: hashMapOf() para crear un mapa de tipo LinkedHashMap., donde puedes consultar el orden en que los elementos fueron insertados, y sortedMapOf() para SortedMap, en el cual todas las entradas se almacenan en un orden de clave ascendente.

Colecciones en Kotlin: introducción

video[Tutorial Colecciones en Kotlin: introducción

Anteriormente, en este tutorial hemos visto cómo declarar rangos en Kotlin mediante los operadores .., o su equivalente rangeTo(), downTo(), o in. Todos ellos trabajan con Iterables, una de las grandes mejoras que presenta Kotlin respecto a Java.
Las colecciones son usadas para almacenar grupos de objetos. Vamos a poder almacenar, recuperar u organizar objetos utilizando una estructura de datos determinada. Kotlin proporciona su API de colecciones como una librería estándar construida encima del API de colecciones de Java.
Hay que indicar que estas librerías son enlazadas a sus implementaciones en tiempo de compilación y, al contrario que otras partes de Kotlin, no podemos ver el código fuente de su implementación, porque sus colecciones son de hecho implementadas por las colecciones Java estándares tales como ArrayList, Maps, HashMap, Set, HashSet, List, etc.
En Kotlin encontramos dos clases de colecciones: las mutables, las cuales nos permiten modificarlas ya sea añadiendo, eliminando o reemplazando un elemento, y las inmutables, que no podrán ser modificadas. En comparación con Java, podremos hacer mucho más con menos código (figura 1).

Nota: En realidad podremos añadir, eliminar o modificar elementos de una colección inmutable mediante funciones de extensión (también conocidas como funciones de operador), pero estas terminarán generando una nueva colección.

Como se muestra a la derecha, si queremos permitir que una variable pueda ser null, tendremos que definirla añadiendo ? a su tipo de datos.
Dado que los errores de tipo NullPointerException se producen cuando intentamos acceder a un método o una propiedad de una variable nula, Kotlin evitará dichos errores denegando la llamada a los métodos o el acceso a las propiedades afectas. Sin embargo, en determinadas ocasiones será necesario utilizar esos métodos o propiedades. Para ello, disponemos de diversas alternativas.

Principales interfaces

La interfaz Iterable Kotlin está encima de la clase de jerarquía de colecciones. Esta interfaz habilita a las colecciones para ser representadas como una secuencia de elementos (que pueden ser iteradas, naturalmente).
interface Iterable<out T> {
   operator fun iterator(): Iterator<T>
}  
La interfaz Collection de Kotlin extiende a la interfaz Iterable, y se trata de una interfaz inmutable (las interfaces Set y List en Kotlin extienden a esta interfaz).
interface Collection<out E> : Iterable<E> {
   override fun iterator(): Iterator<E>
   val size: Int
   fun isEmpty(): Boolean
   operator fun contains(element: E): Boolean
   public fun containsAll(elements: Collection<E>): Boolean
} 
La interfaz MutableIterable nos da un iterador mutable especializado de la interfaz padre Iterable.
interface MutableIterable<out T> : Iterable<T> {
    override fun iterator(): MutableIterator<T>
}  
La interfaz MutableCollection habilita a las colecciones para ser mutables. En otras palabras, nos permite agregar o eliminar componentes en una colección dada. Esta interfaz extiende a la interfaz Collection y la interfaz MutableIterable. Las interfaces MutableSet y MutableList extienden de ella. Como se muestra a continuación, añade a las funciones de Collection nuevas para cambiar elementos:
interface MutableCollection<E>: Collection<E>, MutableItera-ble<E> {
    override fun iterator(): MutableIterator<E>
    fun add(element: E): Boolean
    fun addAll(elements: Collection<E>): Boolean
    fun remove(element: E): Boolean
    fun removeAll(elements: Collection<E>): Boolean
    fun retainAll(elements: Collection<E>): Boolean
    fun clear()
}  

Funciones de extensión sobre colecciones

En Java disponíamos de gran cantidad de métodos estáticos para trabajar con colecciones (java.util.Collections). Kotlin nos proporciona las funciones de extensión que nos permiten usar una sintaxis mucho más intuitiva.
Collections.swap(lista, 2, 5); 
lista.swap(2, 5)  
Además, el uso de lambdas nos va a proporcionar mucha potencia para mani-pular listas. Echemos un vistazo a algunas de las funciones disponibles:
  •     last() devuelve el último elemento en una lista o conjunto.
  •   first() devuelve el primer elemento de una colección.
  •   count() cuenta los elementos de una colección.
También podemos proveer un predicado para buscar dentro de un subconjunto de elementos.
val lista: List<String> = listOf("uno", "dos", "tres")
println(lista.last())  // "tres"
println(lista.last{ it.length == 3 }) // "dos"
println(lista.count()) // 3
println(lista.count{ it.length == 3 }) // 2
val conjunto: Set<Int> = setOf(3, 6, 5, 5, 5, 3)
println(conjunto.last()) // 5 
Nota: En un conjunto last() devuelve el último elemento añadido. El último 3 no se ha añadido dado que ya estaba en el conjunto.
  •    all() devuelve true si todos los elementos cumplen una condición.
  •    any() devuelve true si al menos un elemento cumple una condición.
  •    none() devuelve true si ningún elemento cumple una condición.
val mapa: Map<Int, String> = mapOf(1 to "uno", 2 to "dos", 3 to "tres")
if (mapa.all{ it.key < 5}) println("todas la claves < 5")
if (mapa.any{ it.value == "dos"}) println("un valor es 'dos'") 
  •    drop() devuelve una nueva lista o conjunto conteniendo todos los elementos excepto los primeros n elementos.
val lista: List<String> = listOf("uno", "dos", "tres")
print(lista.drop(2)) // "tres" 
  •    plus() devuelve una colección conteniendo todos los elementos del original y después el elemento dado si no está ya en la colección. Esto terminará creando una nueva lista en vez de modificar la existente.
  •    minus() devuelve una nueva colección conteniendo todos los elementos del conjunto original excepto el elemento dado.
  •    max() devuelve el elemento más grande de la colección.
  •    average() devuelve el valor promedio de los elementos en la colección.
val lista: List<Int> = listOf(1, 3, 4)
println(lista.plus(6))   // [1, 3, 4, 6]
println(lista.minus(3))  // [1, 4]
println(lista.max())       // 4
println(lista.average()) // 2.6666666666666665 
Aquí solo hemos expuesto un pequeño conjunto de funciones. Si quieres conocer todas las funcionalidades que ofrecen las colecciones de Kotlin, te reco-mendamos que consultes su documentación oficial.

El operador let en Kotlin

video[Tutorial El operador let en Kotlin

Durante la programación de aplicaciones nos encontraremos con la siguiente situación en varias ocasiones:

var propiedad: Int? = 42
fun unMetodo() {
    if (propiedad != null) {
        print(propiedad) // Error: aunque la acabamos de comprobar es null 
    }
} 
Y es que, dado que estamos utilizando una variable mutable (propiedad), el valor de la misma podrá ser cambiado por otro hilo, justo después de haber hecho la comprobación. Una alternativa rápida, que es utilizada por el sistema al importar código Java, es utilizar el operador !! pero, como hemos comentado, es un operador que debemos evitar en la medida de lo posible.
Otra posible solución sería crear una copia de solo lectura de dicha varia-ble:
var propiedad: Int? = 42
fun unMetodo() {
    val copia = propiedad
    if (copia != null) {
        print(copia)
    }
} 
El código anterior compila, pero una de las ventajas de Kotlin es la reduc-ción de código innecesario (boilerplate code) y dicho código no encaja con esta filosofía, ya que lo único que hacemos es añadir más líneas de código.
Una alternativa más adecuada será utilizar el operador let, el cual, internamente, crea una copia de la variable a comprobar en una variable inmutable local que será utilizada en el bloque definido a continuación. De este modo, podríamos pensar en una posible solución como la siguiente:
var propiedad: Int? = 42
fun unMetodo() {
    propiedad.let {
        print(it) 
    }
}  
En este ejemplo, la función let verifica si propiedad es null. En caso de no serlo, realiza una copia de propiedad en it y ejecuta el Lambda.
Esta solución es válida, pero si dentro del bloque let necesitamos llamar a una función con su valor, puede que nos encontremos con un error.
var propiedad: Int? = 42
fun unMetodo() {
    propiedad.let {
        imprimir(it) // error
    }
}
fun imprimir(int: Int) {
    print(int)
}  
En este caso, nuestra función imprimir necesita un valor de tipo Int y recibe un valor de tipo Int?. Por suerte, este tipo de error es muy sencillo de solventar con el operador ?., el cual nos va permitir llamar de un modo seguro a nuestro operador let.
var propiedad: Int? = 42
fun unMetodo() {
    propiedad?.let {
        imprimir(it)
    }
}
fun imprimir(int: Int) {
    print(int)
}  
La función let es más compleja. Si nos fijamos en su implementación, ve-remos que, además de la variable con la que llamamos a la función T, tenemos un valor de retorno R.
inline fun <T, R> T.let(block: (T) -> R): R 
Veamos un código más completo donde mostrar la interacción de nuestra clase con el resto del código:
class MiClase {
    var propiedad: Int? = 42

    fun unMetodo() {
        val valor = propiedad?.let {
            imprimir(it)
            "correcto"
        }
    }
    fun imprimir(i: Int) {
        print(i)
    }
} 
En este ejemplo, aparte de imprimir el valor de la variable, devolvemos una cadena de texto indicando que se ha ejecutado el bloque del let. Pero ¿qué pasa en caso de que propiedad sea null?
Es una buena práctica de programación cubrir esta vía, para lo que podemos recurrir al operador Elvis. De este modo, si la variable existe la capturaremos y la usaremos, y si es null mostraremos un mensaje de error:
fun unMetodo() {
    propiedad?.let {
        imprimir(it)
    } ?: run {
        mostrarError()
    }
} 
De este modo, podremos utilizar el valor devuelto por bloque let para in-formar al usuario, algo que sin el bloque de Elvis no era posible.
class MiClase {
    var propiedad: Int? = 42
    fun unMetodo() {
        val valor = propiedad?.let {
            imprimir(it)
            "correcto"
        } ?: run {
            imprimir("ERROR")
            "sin Valor"  
        }
    }
    fun imprimir(i: Int) {
        print(i)
    }
    fun imprimir(string: String) {
        print(string)
    }
} 
Preguntas de repaso: Tratamiento de null

Tratamiento de null en Kotlin

video[Tutorial Tratamiento de null en Kotlin

Uno de los errores más comunes en programación es el odiado NullPointerException. Ocurre cuando el programador olvida inicializar algún objeto o este no ha podido hacerlo por algún tipo de problema. Para tratar de minimizar al máximo la aprición de este tipo de error Kotlin incorpora una serie de medidas, que vamos a tratar en este apartado.
Kotlin soporta la gestión de null de forma nativa, dándonos la posibilidad de definir si una variable puede ser nula o no. De este modo, el compilador puede detectar posibles errores del tipo NullPointerException en tiempo de compilación, reduciendo la posibilidad de que se produzcan en tiempo de ejecución. Por defecto, todas las variables en Kotlin son non-nullable. De este modo, si intentamos asignar un valor null a cualquier variable, el compilador lanzará un error:

var saludo: String = "Hola"
saludo = null // Error compila-ción 
var saludoNullable: String? = "Ho-la"
saludoNullable = null // Compila 
Como se muestra a la derecha, si queremos permitir que una variable pueda ser null, tendremos que definirla añadiendo ? a su tipo de datos.
Dado que los errores de tipo NullPointerException se producen cuando intentamos acceder a un método o una propiedad de una variable nula, Kotlin evitará dichos errores denegando la llamada a los métodos o el acceso a las propiedades afectas. Sin embargo, en determinadas ocasiones será necesario utilizar esos métodos o propiedades. Para ello, disponemos de diversas alternativas.

Añadiendo una comprobación previa

Podemos comprobar si una variable es nula antes de realizar una operación, de igual forma como hacíamos en Java.
val nombreNullable: String? = "Juan"
if (nobreNullable != null) {
    println("Hola, ${nombreNullable.toUpperCase()}.")
    println("Tu nombre tiene ${nombreNullable.length} caracteres.")
} else {
    println("Hola, invitado")
} 
Si intentamos acceder a una variable nullable sin esta verificación ocurre un error de compilación. Para que compile hemos de usar el código de la derecha.
val s: String? = "Hola"
print(s.length) // No compila 
if (s != null) {
   print(s.length) // compila
} 

Llamada segura mediante operador ?.

La comprobación anterior es sencilla, pero necesita de mucho código para realizarla. Kotlin nos ofrece el operador ?. de cara a reducir ese código, de modo que podemos tener la comprobación de null y la llamada al método en la mis-ma línea. De este modo, el siguiente código:
nombre?.toUpperCase()  
if (nombre != null)
     nombre.toUpperCase()
else null 
Que es equivalente al código de la derecha.
Este operador nos va a permitir múltiples comprobaciones simultáneas:
val ciudadActual: String? = usuario?.direccion?.ciudad 
En el ejemplo, ciudadActual será null si usuario, direccion o ciudad son null.

El operador Elvis ?:

El operador Elvis se utiliza para ofrecer un valor por defecto cuando la variable a validar contiene null. Por ejemplo, el siguiente código:
nombre ?: "desconocido"
if (nombre != null) nombre
else                "desconocido" 
Que es equivalente al código de la derecha.
Uno de los usos más comunes de este operador es devolver un valor por defecto distinto a null cuando un método o propiedad es null:
val nombreMayuscula = nombre?.toUpperCase() ?: "DESCONOCIDO"
val ciudadActual = usuario?.direccion?.ciudad ?: "desconocida" 
La complejidad de las expresiones a la izquierda del operador Elvis puede ser incluso mayor, como se muestra en el segundo ejemplo.
Además, podemos utilizar la parte derecha del operador para lanzar una excepción o devolver un valor. Esto nos será de gran utilidad cuando evalua-mos las precondiciones de una función.
val n = nombreNullable ?: throw IllegalArgumentException("Nombre es null") 
Si además de devolver un valor necesitamos ejecutar múltiples operaciones a la derecha del operador elvis, será necesario incluirlas en un bloque de tipo run.
val a = b ?: run {
    val valorSiBEsNull = c.obtenValor()
    almacena(valorSiBEsNull)
    valorSiBEsNull
} 

El operador !!

Mediante el operador !! podemos saltarnos la comprobación de nulidad de una variable, de modo que se lanzará una excepción del tipo NullPointerException en caso que de la variable sea nula.
val nombre: String? = null
nombre!!.toUpperCase() // Produce un NullPointerException 
Se utiliza en aquellas ocasiones en que nosotros como programadores es-tamos seguros de que una variable no va a ser nula, aun pudiendo contener dicho valor, y nos reafirmamos ante el compilador. Es bastante común su apari-ción cuando migramos código de Java a Kotlin, pero dado que su filosofía es contraria a lo que intentamos conseguir con Kotlin, debemos evitar su utiliza-ción en la medida de lo posible. Un poco más adelante se describen varias alternativas para eliminar este operador en caso de una migración desde Java.

Interoperabilidad con Java

Kotlin es completamente interoperable con Java, pero Java no permite la nuli-dad como un tipo. Entonces ¿qué pasa cuando llamamos código Java desde Kotlin? En este caso, los tipos de Java, a los que llamaremos Platform Types, se tratan de un modo especial en Kotlin, el cual relajará las comprobaciones de nulidad en tiempo de compilación. Esto implica que toda la responsabilidad de las operaciones que realizamos con dichos datos recae en nosotros como pro-gramadores, por lo que deberemos tomar las medidas oportunas para evitar errores del tipo NullPointerException.
Por ejemplo, si tenemos la siguiente clase en Java:
public class Usuario {
    private final String nombre;
    public Usuario(String nombre) { this.nombre = nombre; }
    public String getNombre() { return nombre; }
} 
Kotlin no conocerá la nulidad de la variable nombre, por lo que permitirá todo tipo de operaciones sobre la misma. Por lo tanto, como el compilador no va aplicar ningún tipo de restricción, podremos tratarla como non-nullable:
val usuarioJava = Usuario(null)
println(usuarioJava.nombre.toUpperCase()) // Posible NullPointerException
println(usuarioJava.nombre.length)        // Posible NullPointerException 
o como nullable:
val usuarioJava = Usuario(null)
println(usuarioJava.nombre?.toUpperCase()) // Posible escritura de null
println(usuarioJava.nombre?.length)        // Posible escritura de null 

Anko: Androd + Kotlin

video[Tutorial Anko Android + Kotlin

Anko es una librería creada en Kotlin por JetBrains que tiene como propósito agilizar la creación de aplicaciones Android. Su nombre ha sido extraído de las dos primeras letras de Android y Kotlin -&gt; Anko. Las características que nos ofrece pueden dividirse en cuatro módulos:

  •    Funciones comunes: Pequeñas funciones de ayuda para lanzar diálogos, intenciones, etc.
  •    Layouts: Creación rápida de layouts dinámicos desde código.
  •    SQLite: Colección de ayudantes para procesar consultas SQLite.
  •    Corutinas: utilidades basadas en la biblioteca kotlinx.coroutines.
En este capítulo nos centraremos en el primer módulo y mostraremos algún ejemplo de los siguientes[1].

Para añadir Anko a tu proyecto en build.gradle(Project) inserta:

buildscript {
    ext.ankoVersion = "0.10.8"
    … 

En build.gradle(Module) inserta la siguiente :

implementation "org.jetbrains.anko:anko:$ankoVersion" 
Si solo quieres utilizar algunas características puedes usar:
// Funciones comunes
implementation "org.jetbrains.anko:anko-commons:$anko_version"
// Layouts
implementation "org.jetbrains.anko:anko-sdk25:$anko_version" 
implementation "org.jetbrains.anko:anko-appcompat-v7:$anko_version"
// SQLite
implementation "org.jetbrains.anko:anko-sqlite:$anko_version"
// Corutinas
implementation "org.jetbrains.anko:anko-sdk25-coroutines:$anko_version"
implementation "org.jetbrains.anko:anko-appcompat-v7-coroutines:$anko_version"
Si utilizas alguna librería de compatibilidad necesitarás:
    // Appcompat-v7 (solo Anko Funciones comunes)
implementation "org.jetbrains.anko:anko-appcompat-v7-commons:$anko_version"
    // Appcompat-v7 (Anko Layouts)
implementation "org.jetbrains.anko:anko-appcompat-v7:$anko_version"
implementation "org.jetbrains.anko:anko-coroutines:$anko_version"
    // CardView-v7
implementation "org.jetbrains.anko:anko-cardview-v7:$anko_version"
    // Design
implementation "org.jetbrains.anko:anko-design:$anko_version"
implementation "org.jetbrains.anko:anko-design-coroutines:$anko_version"
    // GridLayout-v7
implementation "org.jetbrains.anko:anko-gridlayout-v7:$anko_version"
    // Percent
implementation "org.jetbrains.anko:anko-percent:$anko_version"
    // RecyclerView-v7
implementation "org.jetbrains.anko:anko-recyclerview-v7:$anko_version"
implementation "org.jetbrains.anko:anko-recyclerview-v7-coroutines:$anko_version"
    // Support-v4 (solo Anko Funciones comunes)
implementation "org.jetbrains.anko:anko-support-v4-commons:$anko_version"
    // Support-v4 (Anko Layouts)
implementation "org.jetbrains.anko:anko-support-v4:$anko_version"
    // ConstraintLayout
implementation "org.jetbrains.anko:anko-constraint-layout:$anko_version" 

Funciones comunes

Este módulo de Anko incorpora una serie de funciones de extensión que simplifican la realización de algunas tareas comunes. Podemos:
Asignar un escuchador de eventos con un método abreviado:
 
sin Anko
boton.setOnClickListener { … } 
con Anko
boton.onClick { … } 
Arrancar una actividad con extras:
val intent = Intent(this, 
             Activid-ad::class.java)
intent.putExtra("id", 5)
intent.putExtra("nombre", "Juan")
startActivity(intent) 
startActivi-ty<Actividad>("id" to 5, 
                "nombre" to "Juan") 
Ejecutar las intenciones explícitas más comunes:
browse("http://androidcurso.com")
share("compartir", "asunto")
email("correo@upv.es", "asunto", "correo") 
Mostrar diálogos de alerta:
val builder = AlertDialog.Builder(this)
builder.setTitle("Titulo")
builder.setMessage("Mensaje")
builder.setPositiveButton("OK") {…}
build-er.setNegativeButton("Cancel"){…}
builder.show() 
alert(Appcompat,"Titulo","Mensaje") {
  positiveButton("OK") {…}
  negativeButton("Cancel") {…}
}.show() 
Mostrar un Toast:
Toast.makeText(this, "mensaje",    
   Toast.LENGTH_SHORT).show() 
toast("Mensaje") 
Mostrar diálogos Snackbar:
Snackbar.make(
  findViewBy-Id(android.R.id.content),
  "Mensaje", 
  Snackbar.LENGTH_LONG).show() 
longSnackbar(findViewById(
  android.R.id.content), "Men-saje") 
Obtener la ratio de conversión entre dp y píxeles:
val dpAsPx = TypedValue.applyDimension(
    TypedValue.COMPLEX_UNIT_DIP, 10f,
    getResources().getDisplayMetrics()) 
val dpAsPx = dip(10f) 
Escribir código condicional según la versión de SDK:
if (Build.VERSION.SDK_INT == 
   Build.VERSION_CODES.LOLLIPOP){…}
if (Build.VERSION.SDK_INT >= 
   Build.VERSION_CODES.LOLLIPOP){…} 
doIfSdk(Build.VERSION_CODES.LOLLIPOP){…}

do-FromSdk(Build.VERSION_CODES.LOLLIPOP){} 
Ejecutar código en otros hilos:
Thread {
  run() {
    runOnUiThread {
      toast("ejecut. desde otro hilo")
    }
  }
}.start() 
doAsync {
  uiThread {
    toast("ejecutado desde otro hilo")
  }
} 
El código de la derecha no sería 100% equivalente, dado que se basa en la clase Executor en lugar de Thread.

Layouts

Este módulo de Anko permite insertar layouts por código en tiempo de ejecu-ción, en lugar de diseñarlos en XML:
setContentView(R.layout.activity_main)
verticalLayout {
    val nombre = editText()
    button("Escribe tu nombre") {
        onClick { toast("Hola, ${nombre.text}!") }
    }
} 
Más info: https://medium.com/@v.souhrada/introduction-to-anko-for-android-part-1-6178d536cbe6
desafio

Desafío: Usar Anko para crear un layout.

Trata de crear un layout de Audiolibros utilizando Anko en lugar del diseño en XML.

[1] https://www.kotlindevelopment.com/why-should-use-anko/

Clases enumeradas en Kotlin

video[Tutorial Clases enumeradas en Kotlin

Las clases enumeradas (enum) nos permiten declarar un conjunto de valores que puede tomar una variable:
 

sealed class Figura {
   class Triangulo(val lado1:Int, val lado2:Int, val lado3:Int) : Figu-ra()
   class Cuadrado(val lado:Int) : Figura()
   class Circulo(val radio:Int) : Figura()
} 
Al igual que en Java, un enumerado se define por medio de una clase, aunque ahora es más evidente al tener que indicar class. Cada una de las constantes de un enum supone la creación de un objeto. Por lo que en el ejemplo anterior se crearán cuatro objetos. Una variable de tipo Estaciones no es más que una referencia a uno de estos cuatro objetos. Lo estudiamos con detalle en el siguiente punto. Como cada constante es una instancia de la clase, se pueden inicializar:
enum class VersionAndroid(val api: Int) {
    JELLY_BEAN(16), KITKAT(19), MASHMALLOW(23), NOUGAT(24)
} 
Una constante de un enumerado también puede declarar sus propios métodos y propiedades:
enum class Estacion {
   PRIMAVERA { override fun temperatura() = 24.0 },
   VERANO { override fun temperatura() = 32.2 };
   abstract fun temperatura(): Double
   var diasSoleados: Int = 0
} 
Si un enumerado define algún miembro, este ha de separarse de las constantes con un punto y coma, del mismo modo que hacíamos en Java. Igualmente, las clases enum en Kotlin tienen métodos sintéticos que permiten listar las constantes definidas (values()) y obtener una constante por su nombre (valueOf()). Además, en cada constante se definen las propiedades name y ordinal:
 

Comparativa clases enumeradas y selladas

Las clases selladas se asemejan mucho a las clases enumeradas, pero no son exactamente lo mismo. Las clases selladas son una extensión de las cla-ses enumeradas, ya que el posible conjunto de valores de un tipo enumerado también están restringidos, pero mientras que cada constante del tipo enume-rado solo existe como una única instancia, una subclase de una clase sellada puede tener múltiples instancias.
La forma de implementar un enum es similar a una clase sellada donde to-dos sus elementos son objetos:
enum class Estacion { 
   PRIMAVERA, 
   VERANO, 
   …
} 
sealed class Estacion() {
   object PRIMAVERA: Estacion() 
   object VERANO: Estacion() 
   …
} 
El mejor modo de ver las diferencias es a través de un ejemplo:
enum class Direcciones { ARRIBA, IZQUIERDA, DERECHA, ABAJO }
sealed class Intention {            
   object None : Intention()
   object Refresh : Intention()
   data class Error(val reason: String) : Intention()
   data class LoadContent(val content: List<String>) : Intention()
} 
Ambas tienen el mismo comportamiento que una clase abstracta, evitando una instanciación directa de las mismas, a la vez que nos permiten declarar métodos abstractos:
enum class Direcciones {
   ARRIBA   { override fun direccion(x: Int, y: Int) = x to (y - 1) },
   IZQUIERDA{ override fun direccion(x: Int, y: Int) = (x - 1) to (y) },
   DERECHA  { override fun direccion(x: Int, y: Int) = (x + 1) to (y) },
   ABAJO    { override fun direccion(x: Int, y: Int) = x to (y + 1) };
   abstract fun direccion(x: Int, y: Int): Pair<Int, Int>
}
sealed class Intention {
   object None : Intention() {
      override fun log() { println("none") }
   }
   object Refresh : Intention() {
      override fun log() { println("refresh") }
   }
   data class Error(val reason: String) : Intention() {
      override fun log() { println("error") }
   }
   data class LoadContent(val content: List<String>) : Intention() {
      override fun log() { println("loadContent") }
   }
   abstract fun log()
} 
Por otro lado, una clase enumerada únicamente puede crear una instancia de cada constante, mientras que podremos tener varias instancias de las sub-clases de una clase sellada. Dependiendo de lo que queramos hacer podre-mos utilizar una u otra. Esto es, si necesitamos un comportamiento constante, utilizaremos una clase enumerada y, en caso contrario, una clase sellada.
Preguntas de repaso: Clases selladas y enumeradas

Clases selladas en Kotlin

video[Tutorial Clases selladas en Kotlin

Las clases selladas (sealed) se utilizan para representar una jerarquía de cla-ses que heredan de una clase padre, de forma que tanto la clase padre como las anidadas se definen de forma conjunta en un mismo fichero. Imaginemos que queremos definir la clase padre Figura de la que extienden las subclases Triangulo, Cuadrado y Circulo. Como ves es una técnica para hacer polimorfismo.

sealed class Figura {
   class Triangulo(val lado1:Int, val lado2:Int, val lado3:Int) : Figu-ra()
   class Cuadrado(val lado:Int) : Figura()
   class Circulo(val radio:Int) : Figura()
} 
A partir de Kotlin 1.1 ya no es necesario que las clases hijo estén anidadas dentro de la clase padre:
sealed class Figura 
class Triangulo(val lado1:Int, val lado2:Int, val lado3:Int) : Figura()
class Cuadrado(val lado:Int) : Figura()
class Circulo(val radio:Int) : Figura() 
Una clase sellada se declara como abstracta, cuando creemos instancias de esta clase estamos obligados a usar alguno de sus hijos. Además, una cla-se sellada ha de definirse en un solo fichero, por lo que el número de clases hijas no podrá ser ampliada en un futuro. Lo que significa que no podremos ampliar el tipo de figuras. Por esta razón se conocen como selladas. Gracias a este hecho, cuando las utilizamos con una expresión when, como es posible verificar que se cubren todos los casos, no será necesario añadir la cláusula else.
val figura: Figura = Triangulo()
val texto = when (figura) {
   is Triangulo -> "triangu-lo"
   is Cuadrado -> "cuadrado"
   is Circulo -> "circulo"
   else -> "sin definir" 
} 
val figura:Figura = Figu-ra.Triangulo()
val texto = when (figura) {
   is Figura.Triangulo -> "trian-gulo"
   is Figura.Cuadrado -> "cuadra-do"
   is Figura.Circulo -> "circulo"
   else -> "sin definir" 
} 
Nota: Se ha puesto dos ejemplos según usemos clases anidadas o no.
En el caso de que estemos interesados en contemplar la posibilidad de que una figura no esté definida, o incluso, un tipo de figura que no requiera alma-cenar un estado, añadimos a nuestra clase sellada elementos object:
sealed class Figura 
class Triangulo(val lado1:Int, val lado2:Int, val lado3:Int) : Figura()
…
object Punto : Figura()
object SinDefinir : Figura() 
tendremos que incorporar estas opciones al bloque when:
val texto = when (figura) {
   is Triangulo -> "triangulo"
   …
   Punto -> "punto"
   SinDefinir -> "sin definir"
} 
Observa  que las nuevas opciones ya no son clases, por lo que no usaremos is en la comparación. Para entender la diferencia entre class y object vamos a ver cómo trabaja internamente. Cuando se trata de una Figura de tipo Triangulo, Cuatrado o Circulo trabajará de forma normal. Es decir, se creará un nuevo objeto en memoria con todas sus propiedades. Pero cuando sea Punto o SinDefinir, trabajará de forma diferente. La primera vez que se necesite se creará una instancia del objeto. Estos objetos solo son creados una vez en memoria. La segunda vez que se necesite una variable con este valor, no se creará un nuevo objeto, sino que esta variable será una referencia al objeto creado anteriormente[1]. Este comportamiento es igual a los enumerados, que veremos en el siguiente apartado. Un object no puede extender la clase con nuevas propiedades, pero sí que puede sobrescribir sus funciones. Veámoslo con un ejemplo:

Práctica: Clases selladas, diferencia entre class y object.

1. En Android Studio accede a Tools / Kotlin / Kotlin REPL.
2. Ejecuta el código siguiente:
sealed class Figura(open var color:Int=0) {
   abstract fun area(): Int

   class Cuadrado(override var color: Int,  
          val lado: Int) : Figura(color) {
      override fun area() = lado * lado
   }
   object Punto : Figura(1) {
      override fun area() = 0
      init {print("Punto Creado, ")}
   }
} 
val p1 = Figura.Punto
val p2 = Figura.Punto
val c = Figura.Cuadrado(2,8)

p1.color = 3
print("color c=${c.color}, ")
print("color p2=${p2.color}") 
3. Observar que la salida es: Punto Creado, color c=2, color p2=3.
4. ¿Cómo explicas que solo aparezca una vez “Punto Creado”?
5. ¿Por qué el color de p2 es 3, si lo que hemos cambiado es el color de p1?
6. ¿Por qué el color de c continúa siendo 2?
7. ¿Cómo podemos conseguir que cada punto tenga su propio color?

 Solución:

Al crearse la clase Figura, se crea una instancia por cada elemento object. En el ejemplo se creará el objeto Punto. La creación de las variables p1 y p2 no supondrán la creación de nuevos objetos, ambas serán referencias al objeto anterior. La inicialización de c, sí que supondrá la creación de un nuevo objeto en memoria, con sus dos propiedades. En la siguiente instrucción se modifica el color de p1, pero como solo hay un color también se modifica el color de p2.

1. Solo se crea un objeto Punto.
2. Las variables p1 y p2 apuntan al mismo objeto.
3. La variable c es un objeto independiente.
4. Utilizaríamos class Punto en lugar de object Punto.

En el ejemplo anterior se definían tipos de figuras. Sin embargo, no es ne-cesario que las diferentes subclases estén relacionadas entre sí. Podemos utilizar una clase sellada para representar una variable que puede tomar valo-res de diferentes tipos, aunque estos tipos no tengan ninguna relación entre sí. Por ejemplo, una solicitud HTTP puede darnos como respuesta una página web o un código de error:
sealed class RespuestaHTTP 
data class Correcta(val contenido: String) : Respuesta()
data class Error(val codigo: Int, val mesaje: String) : Respuesta()

fun getUrl(url: String): Respuesta {
   val valido = …
   if (valido) return Correcta("Contenido…")
   else        return Error(404, "No encontrada")
}

val respuesta = getUrl("/")
when (respuesta) {
   is Correcta -> println(respuesta.contenido)
   is Error -> println(respuesta.mesaje)
} 
[1] Esta forma de trabajar corresponde con el patrón Singleton
 

Tipos de clases en Kotlin: abiertas, abstractas, internas y con alias

video[Tutorial Tipos de clases en Kotlin abiertas, abstractas, internas y con alias

Al igual que sucede con Java, las clases abstractas de Kotlin son clases que no pueden ser instanciadas. Su finalidad es utilizarlas de cara a proveer una plantilla común para otras clases que las extiendan.
La forma de declarar una clase abstracta es mediante la palabra reservada abstract. Una clase abstracta podrá contener propiedades y funciones tanto abstractas (marcándolas con la palabra reservada abstract) como no abstractas:

abstract class Vehiculo(val nombre: String,
                        val color: String,
                        val peso: Double) {   // Propiedades no abstrac-tas
   abstract var maxVelocidad: Double   // Propiedad abstracta
   abstract fun arrancar() // función abstractos
   abstract fun parar()
   fun muestraDetalless() {  // función no abstracto
      println("Nombre: $nombre, Color: $color, Velicidad: $maxVelocidad ")
   }
} 
Cualquier subclase que extienda de una clase abstracta deberá implementar todos los métodos y variables abstractas de la misma, o bien declararse a su vez como abstracta.
De acuerdo a lo que vimos en la herencia de clases, sería necesario utilizar la palabra reservada open para indicar que podemos heredar de esa clase. Sin embargo, con las clases abstractas no es necesario hacerlo, ya que incorporan esa propiedad por defecto. Veamos un ejemplo de cómo extender una clase abstracta:
class Coche(nombre: String, color: String, peso: Double,
       override var maxVelocidad: Double): Vehiculo(nombre, color, peso) {
   override fun arrancar() {
      println("Coche arrancado")
   }
   override fun parar() {        
      println("Coche parado")
   }
} 

Clases anidadas e iternas

En Kotlin una clase puede declararse dentro de otra dando lugar a una clase anidada.
class Externa {
   private val propiedad: Int = 1
   class Anidada {
      fun funcion() = 2
   }
} 
class Externa {
   private val propiedad: Int = 1
   inner class Interna {
      fun funcion() = propiedad
   }
}  
Si a una clase anidada le anteponemos el modificador inner la transforma-mos en interna, con lo que conseguimos que la clase pueda acceder a todos los miembros de la clase externa.

Type Alias

Una de las cosas interesantes que podemos hacer en Kotlin es asignar un alias a un tipo de dato, siendo de gran utilidad para acortar el nombre de tipos de datos cuando son muy largos o si queremos aclarar el significado:

typealias GrupoPersonas = Set<Persona>
typealias TablaFicheros<K> = MutableMap<K, Mutable-List<File>> 

Incluso se puede aplicar a tipos de función:
 

typealias Manejador = (Int, String, Any) -> Unit
typealias Predicado<T> = (T) -> Boolean 

Clases de datos en Kotlin

video[Tutorial Clases de datos en Kotlin

Posiblemente estés acostumbrado a utilizar clases POJO en tus proyectos. Son clases muy simples donde se describe la información necesaria para representar un objeto, pero que entran en interacciones con otras clases o requieren de API externos.
Por lo general, un POJO solo nos proporciona una colección de campos. Aunque también podemos añadir funciones que solo involucren objetos de esta clase. Por ejemplo, la clase PuntoGeografico, podría tener como campos longitud y latitud y una función que nos calcule la distancia entre dos puntos.
Kotlin nos ofrece las clases de datos para poder crear rápidamente POJO. Veamos un ejemplo:

data class Complejo(var real: Double, 
                    var imaginario: Double=0.0) 

Toda clase de datos incorpora las siguientes funciones:

     equals(): compara dos objetos.
       hashCode(): código hash de un objeto usado por el método anterior.
     copy(): copia un objeto.
      toString(): convierte objeto a string.
       component1(), component2(), …: componentes en orden de declaración.

Si una propiedad es declarada en el cuerpo de la clase, en lugar de en el constructor primario, ya no tendrá los beneficos indicados:
 

data class Complejo(var real: Double, 
                    var imaginario: Double=0.0) {
    val irracional: Boolean = false
} 

En en el ejemplo anterior la propiedad irracional es ignorada en las funciones equals(), hashCode(), copy(), toString() y componentX().
Las clases de datos deben cumplir los siguientes requisitos:

  •   El constructor primario ha de tener al menos un parámetro.
  •   Todos los parámetros del constructor principal deben marcarse como val o var.
  •   Las clases de datos no pueden ser abstractas, abiertas, selladas o internas

Al copiar un objeto podemos cambiar algunas de sus propiedades:

val c = Complejo(1.0, -1.0)
val c2 = c.copy(im = 0.0) 

Declaraciones desestructuradas

Desestructurar es el procedimiento por el cual podremos extraer múltiples valo-res que se encuentran almacenados en objetos y vectores. En determinadas ocasiones es necesario desestructurar un objeto en diferentes variables. Se realiza de la siguiente manera:
val c = Complejo(1.0, -1.0); 
val (re, im) = c 
De esta forma podremos acceder a cada uno de los valores de una clase de datos utilizando una sola asignación. Para acceder a cada propiedad se definen las funciones component1(), component2(), … El código de la izquierda es compilado a uno similar al de la derecha.
val (re, im) = c 
val re = c.component1()
val im = c.component2() 
Si algún valor no nos iteresa usamos _ :
val (_, im) = c 
val im = c.component2()  
Podremos utilizar la desestructuración para devolver varios valores de una función:
data class Resultado(val res1: String, val res2: Int)

fun funcion(): Resultado {
    val res1 = "texto"
    val res2 = 3
    return Resultado(res1, res2)
}
// Para usar la función
val (res1, res2) = funcion()  
Primero definimos una clase de datos que tenga como atributos los tipos a devolver, de forma que la función ha de devolver esta clase.
Otra de las utilizaciones más comunes de la desestructuración es para descomponer objetos almacenados en un Map por su identificador clave y valor.
val mapa = HashMap<String, Int>()
for ((clave, valor) in mapa) { … } 

Ejercicio: Migrando la clase Libro de Audiolibros.

A lo largo de esta unidad vamos a migrar el proyecto Audiolibros desarrollado en las unidades 1 y 2 desde Java a Kotlin. En este ejercicio empezamos convirtiendo el POJO Libros en una clase de datos Kotlin.

1. Haz una copia de la carpeta del proyecto Audiolibros y llámala AudiolibrosKotlin.
    
2. Selecciona el menú Tools / Kotlin / Configure Kotlin in Project. Selecciona Android with Gradle y luego los siguientes valores:
3. Abre la clase Libro y selecciona Code / Convert Java File to Kotlin File.
4. Ejecuta el proyecto y verifica que funciona correctamente.
5. La transformación no es todo lo buena que podríamos desear. Vamos a mejorar algunas partes del código. Primero reemplaza class Libro por data class Libro. De esta forma tendremos funciones adicionales para copiar libros, recorrer sus campos, etc.
6. Reemplaza var por val en todos los campos menos novedad y leído. Estos campos no queremos que cambien una vez inicializado un libro.
7. Los campos novedad y leído. los marca como de tipo Boolean?. Puedes eliminar el ?. Este concepto se explica más adelante. Reemplaza los valores por defecto por los siguientes:
data class Libro(val titulo: String,
                 val autor: String, 
                 val urlImagen: String,
                 val urlAudio: String,
                 val genero: String,          // Género literario
                 var novedad: Boolean = true, // Es una novedad
                 var leido: Boolean = false   // Leído por el usuario 
8. Ejecuta el proyecto y verifica que funciona correctamente.
9. El concepto de compation object será explicado más adelante. De momento, mueve el resto de constantes al principio (entre import y class). De esta forma se convierten en constantes de paquete en lugar de clase. Antepón const a las constantes para indicar que su valor ya es conocido en tiempo de compilación.
10. Mueve la función ejemploLibros() tras las constantes. Así será una función estática de paquete en lugar de una función.
11. En el resto del proyecto habrá que cambiar la forma de acceder a las constantes y la función. Por ejemplo, en lugar de escribir Libros.compa-nion.ejemploLibros() directamente escribiremos ejemploLibros(). Sabrás dónde has de hacerlo al aparecer errores de compilación.
12. Puedes eliminar el compation object. Ya no es utilizado.
13. Ejecuta el proyecto y verifica que funciona correctamente.
 

Ejercicio: Migrando la clase MainActivity de Audiolibros.

Vamos a realizar el mismo proceso que en el ejercicio anterior, pero ahora con una actividad:
1. Abre la clase MainActivity y selecciona Code / Convert Java File to Kotlin File. Tras la operación es posible que tengas que añadir algún import adicional.
2. Ejecuta el proyecto y verifica que funciona correctamente.
3. Una de las ventajas de Kotlin a la hora de trabajar con actividades es que ya no va a ser necesario usar findViewById para acceder a las diferentes vistas del layout. Para poder usar esta facilidad, abre build.gradle (Module:app) y añade la línea subrayada:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' 
4. Elimina las siguientes líneas:
private var tabs: TabLayout? = null
…
override fun onCreate(savedInstanceState: Bundle?) {
   …
   tabs = findViewById<View>(R.id.tabs) as TabLayout
Este trabajo es realizado de forma automática por el sistema. Has de tener en cuenta que el nombre del objeto creado coincide con el id declarado en el layout.
5.  Ejecuta el proyecto y verifica que funciona correctamente.
6.  Repite la misma operación para el resto de Vistas del Layout.
7.  Ten en cuenta que alguna vista (como drawer) tiene un id en el layout dife-rente (drawer_layout). En tal caso, tendrás que cambiar uno de los dos nombres.
8.  En la función onNavigationItemSelected reemplaza la lista de if else por una sentencia when.
9.  Busca otros cambios que puedan resultar convenientes.

Clases en Kotlin

video[Tutorial Clases en Kotlin

Cuando declaramos una clase en Kotlin podemos indicar parámetros que son utilizados para definir un constructor por defecto. Si indicamos var o val en estos parámetros pasarán a formar parte de las propiedades de la clase:

public class Complejo {
   private double re, im;
   public Complejo(double re, 
                  double im) {
      this.re = re; this.im = im;
   }
   public void suma(Complejo v) {
      re = re + v.re;
      im = im + v.im;
   }
} 
class Complejo(var re: Double, 
               var im: Dou-ble=0.0){
     
   fun suma(v: Complejo) {
      re = re + v.re;
      im = im + v.im;
   }
}   
Si no se indica lo contrario, una clase extiende de Any, que es similar a Object en Java. Las clases en Kotlin por defecto son públicas.
También podemos declarar propiedades e inicializarlas a partir de los parámetros. La siguiente clase tiene tres propiedades nombre, edad y clavePersona:
class Persona(nombre: String, var edad: Int) {
   val clavePersona = nombre.toUpperCase()
}
Si necesitamos añadir instrucciones para inicializar el objeto podemos utilizar un bloque init. También podríamos añadir constructores alternativos siempre que utilicen parámetros diferentes
class Persona(val nombre: String) {
   init {
      println("Inicialización objeto")
   }
   constructor(nombre: String, edal: Int) : this(nombre) {
      println("Inicialización segundo constructor")
   }
} 
Para crear objetos no es necesario usar new. Tampoco es imprescindible indicar el tipo del objeto, este se deduce del constructor.
 
Persona p = new Persona("Ana"); 
 val p = Persona("Ana") 
Para acceder a las propiedades de un objeto ya no es necesario definir getters y setters. Estos son creados de forma automática:
public class Persona {
   private String nombre;
   public String getNombre() {
      return nombre;
   }
   public void setNombre(String n){
      nombre = n;
   }
} 
 class Persona {
   var nombre: String = ""
} 
Modificar las propiedades de un objeto es más sencillo en Kotlin, simplemente se indica su nombre y Kotlin realizará la llamada al getter o setter. Gracias a esto, el código resulta mucho más legible:
 
p.setNombre("Ana");
String s = p.getNombre(); 
p.nombre = "Ana"
var s = p.nombre 
Kotlin también nos permite crear nuestros propios getters y setters.
class Persona {
   var nombre: String = ""
      get() = field.toUpperCase()
      set(value) {
         if (value=="Osama bin Laden") llamarFBI()
         field = value
      }  
} 
En un getter y setter hemos de utilizar field para acceder a la propiedad.

En Kotlin no es necesario que una propiedad se almacene en una variable de respaldo. Veamos un ejemplo:
 
class Persona {
   var nombre: String = ""
   var longNombre: Int
      get() = nombre.size
}  
En la segunda propiedad no se creará una variable de tipo Int. Esto ocurre siempre que se implemente get o set y que, además, en ellos no se haga referencia a fiel (como ocurre en el ejemplo anterior).
Por defecto, tanto la clase como sus propiedades y funciones son públicos. Si queremos que algo no sea accesible desde fuera de la clase antepondremos private. Si queremos que solo el getter o el setter sean privados lo indicaremos de la siguiente manera:
class Persona {
   private var edad: Int = 0
   var nombre: String = ""
      private set
}  
Las clases anteriores no pueden usarse como base para crear nuevas clases; por defecto son cerradas. (En Java una clase cerrada se marca como final.) Si queremos heredar de una clase hemos de anteponer open.
open class Padre(p: Int) 
class Hijo(p: Int) : Padre(p) 
Si la clase no tiene un constructor primario, cada constructor secundario tiene que inicializar el tipo base con la palabra clave super:
class MyView : View {
    constructor(ctx: Context) : super(ctx)
    constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}  
Por defecto, las propiedades y métodos de una clase son cerrados. (En Java marcados como final.) Para poder sobreescribirlos hemos de indicar open:
open class Padre {
    open var nombre = "Soy padre"
    open fun abierto() {}
    fun cerrado() {}
}

class Hijo() : Padre() {
    override var nombre = "Soy hijo"
    final override fun abierto() {}
} 
 Un miembro marcado con override continúa siendo abierto. Para cerrarlo hay que anteponer final.
Para acceder a propiedades y métodos de la clase padre usaremos super.
class Hijo() : Padre() {
    override var nombre = "hijo de ${super.nombre}"
    final override fun abierto() {
        super.abierto()
    }
} 

Extensiones

Las extensiones nos permiten ampliar la funcionalidad de una clase sin utilizar la herencia. Kotlin admite funciones de extensión y propiedades de extensión. Veamos un ejemplo:
 

fun MutableList&lt;Int&gt;.swap(index1: Int, index2: Int) {
    val tmp = this[index1] // 'this' corresponde con la lista
    this[index1] = this[index2]
    this[index2] = tmp
} 

Desde un punto de vista sintáctico es como si hubiéramos añadido un método a la clase MitableList&lt;Int&gt; y todos sus descendientes. Pero la función de extensión no modifica la clase, sino que se resuelve de forma estática.
En Java estarás acostumbrado a utilizar clases *Utils, como FileUtils, StringUtils o java.util.Collections. Estas clases extienden la funcionalidad de otras sin usar la herencia. La sintaxis resulta menos legible que en Kotlin:

Collections.swap(lista, 2, 5); 
lista.swap(2, 5) 
También podemos extender una clase con nuevas propiedades. Veamos un ejemplo:
val <Int> List<Int>.lastIndex: Int
    get() = size – 1 
 

Funciones inline en Kotlin

video[Tutorial Funciones inline en Kotlin

En una función normal su código es almacenado una vez en memoria. Cada vez que es invocada, se añaden los parámetros en la pila y se invoca.
En una función inline no se realiza esta invocación, sino que el código de la función es insertado cada vez que se utiliza.
En teoría, una función inline tiene mayor rendimiento frente a las normales, debido a que evitan usar la pila para pasar parámetros y evitan las instrucciones de salto y retorno. Esta afirmación va a depender de cómo se compile el código. Por ejemplo, en la máquina virtual Java, usa la pila incluso para ejecutar instrucciones en código máquina. Debido a que el código se tiene que «copiar» cada vez que se llame a dicha función, el tamaño del ejecutable aumentará conforme el tamaño de la función de tipo inline crezca.
Puede resultar conveniente utilizar una función inline si se encuentra en un bucle y va a ser llamada muchas veces. Pero en la mayoría de los casos es mejor no utilizarlas. Para convertir una función inline simplemente anteponemos esta palabra en su declaración:

inline fun suma(a:Int, b:Int) = a + b
val c = suma(2, 2) 
La verdadera ventaja de las funciones inline aparece cuando se aplican a funciones de orden superior. Las funciones de orden superior presentan cierta penalización de rendimiento (tanto en tiempo como en memoria). Para llamar al parámetro Lambda se crea un nuevo objeto en memoria que contiene un método con los parámetros indicados en el Lambda. Por ejemplo, dada la función:
fun opera(l: List, v: Int, funcion: (Int, Int)->Int): List 
al compilar la siguiente llamada:
opera(l, v) { a, b -> a + b } 
El código resultante sería algo parecido como si en Java hubiéramos escrito:
opera(l, v, new Funcion() { 
   @Override public int funcion(int a, b) { return a + b; }
}); 
Lo que supone la creación de un nuevo objeto anónimo[1]. Pero si, por el contrario, hubiéramos escrito:
inline fun opera(l: List<Int>, v: Int, funcion: (Int, Int)->Int): List<Int> 
El inline se aplicará no solo a opera sino también a funcion. El resultado final será insertar directamente el código de opera, y luego dentro de este código reemplazar funcion por su código. Quedaría así:
val result = arrayListOf<Int>()
for (item in lista)
   result.add(funcion(item, valor) item + valor)
return result 
En caso de una lista de 100.000 elementos, hemos evitado la creación de 100.000 objetos. Lo que supone una importante mejora. Unido al hecho de que nos hemos evitado 10.000 llamadas a la función.

Ejercicio: Comparativa Lambdas con y sin funciones inline.

En este ejercicio vamos a tratar de demostrar la influencia de usar Lambdas en funciones normales y en funciones inline. Analizaremos tanto el rendimiento temporal como el uso de memoria.
1. Crea un nuevo proyecto en Android Studio con los siguientes datos:
   Application Name: Funciones Inline
   Package name: com.example.funcionesinline
   - Including Kotlin support
   - Phone and Tablet
      Minimum SDK: API 16: Android 4.1 (Jelly Bean)
   Basic Activity

Deja el resto de valores por defecto.   
2. Añade la siguiente función en MainActivity.
inline fun opera(l: IntArray, v: Int, funcion:(Int, Int)-&gt;Int): IntArray {
    val result = IntArray(l.size)
    for ((indice, item) in l.withIndex())
        result[indice] = (funcion(item, v))
    return result
}
3. En el método onCreate añade dentro de fab.setOnClickListener:
fab.setOnClickListener { view ->
    var lista = IntArray( 5, { 2 } )
    val ini = System.currentTimeMillis()
    for (n in 1..100_000) {
        opera(lista, 2) { a, b -> a+b }
    }
    val fin = System.currentTimeMillis()
    Snackbar.make(view, "Tiempo ${fin-ini}", Snackbar.LENGTH_LONG)
            .setAction("Action", null).show()
} 
 Comenzamos inicializando un array de enteros de 5 elementos y de valor 2. Observa como el valor con el que inicializamos los elementos se indica mediante un Lambda. Anotamos el tiempo de inicio. Realizamos 100.000 llamadas a opera y anotamos el tiempo final. Finalmente mostramos el tiempo transcurrido en un Snackbar.
4. Ejecuta la aplicación y apunta el tiempo transcurrido.
5. Quita el modificador inline de opera.
6. Ejecuta de nuevo la aplicación y apunta el tiempo transcurrido. Este ha de ser significativamente mayor.
7. Para realizar un estudio de la memoria utilizada pulsa en el botón:

8. Haz clic en la parte central MEMORY.
9. Pulsa el botón Record Memory Alocation  .
10. Pulsa el botón flotante de la aplicación y cuando muestre el tiempo transcurrido pulsa en Stop Recording.
11. Captura la pantalla para poder analizarla con posterioridad.
12. Añade el modificador inline de opera.
13. Repite el proceso y compara los resultados ¿Ha habido una diferencia significativa de uso de memoria?El tema de las funciones es más extenso. Por ejemplo, en próximos puntos veremos funciones de extensión.
 
Preguntas de repaso: Lambdas

Lambdas y Variables función en Kotlin

video[Tutorial Lambdas y variables función en Kotlin

Una expresión Lambda es una subrutina de código a la que normalmente no se ha asignado un identificador.
Una expresión Lambda suele ser pasada como argumento a otra función para parametrizar su ejecución. A la función que recibe como parámetro una expresión Lambda se le conoce como función de orden superior.
La mayoría de lenguajes modernos las incorporan. Java dispone de expresiones Lambdas desde la versión 8.
Una expresión Lambda siempre se escribe entre llaves. Si tiene parámetros estos se escriben al principio seguidos de ->. Veamos algunos ejemplos:

{ toast("Mensaje") }           //Llama aun método
{ x: Int, y: Int -> x + y }    //Suma dos enteros
{ x, y -> x + y }              //Suma dos valores
{ x -> x * 2 }                 //Multiplica por dos
{ it * 2 }                     //Si solo hay un parámetro puede usarse it
{ x, _ -> x * 2 }              //_ indica un parámetro que no usamos 
Una expresión Lambda puede ser pasada como parámetro de una función de orden superior. Veamos primero un ejemplo de función de orden superior:
fun opera(lista: List, valor: Int, funcion:(Int, Int)->Int ): List {
   val result = arrayListOf()
   for (item in lista)
      result.add(funcion(item, valor))
   return result
} 
La función opera tiene tres parámetros una lista, un valor y una funcion.  Se va a recorrer todos los elementos de lista y va a utilizar funcion, para realizar un cálculo de cada elemento de la lista y con valor. El resultado obtenido es almacenado en cada posición de la lista de salida.
Para utilizar opera podríamos declarar primero funcion como una variable:
val funcion =  fun (x: Int, y:Int ):Int = x + y
lista = opera(lista, 2, funcion)  
También podemos usar una función anónima, directamente en el parámetro
lista = opera(lista, 2, fun (x, y):Int = x + y)  
Observa como en este caso ya no es necesario indicar los tipos de x e y. Estos pueden ser inferidos del parámetro que están reemplazando.
Esta función también puede ser declarada usando una expresión Lambda:
val lambda =  { x: Int, y:Int -> x + y }
lista = opera(lista, 2, lambda) 
Pero resulta más legible si utilizamos una expresión Lambda:
lista = opera(lista, 2, { x, y -> x + y }) 
Cuando el último parámetro para una función es una Lambda, esta puede especificarse fuera de paréntesis:
lista = opera(lista, 2) { 
    x, y -> x + y
}  
Como acabamos de ver las expresiones Lambda tienen varias utilidades, pero dentro del ámbito de Android nos van a ser de gran utilidad para escribir el código de los escuchadores de eventos.
Como seguramente ya conozcas, escribir un escuchador en Java es muy laborioso. Se suele crear un objeto anónimo que implementa una interfaz con todos sus métodos. Como puedes ver en el código de la derecha, con Kotlin, va a ser mucho más sencillo:
boton.setOnClickListener(
  new View.OnClickListener(){
    @Override
    public void onClick(View v){
       hazAlgo();
    }
});
boton.setOnClickListener { hazAlgo() } 
Esto es posible gracias a que Kotlin hace la siguiente optimización sobre la librería Java de Android. Cada vez que encuentra un parámetro cuyo tipo es una interfaz con una única función, el método es sobrecargado reemplazando el parámetro por una función:
fuc setOnClickListener( escuchador: (View) –> Unit ) 
Nota: Unit es un tipo con un solo valor, el objeto unidad. Corresponde con void en Java.
Es decir, el parámetro escuchador ha de ser reemplazado por una función o expresión Lambda que tenga como parámetro un objeto View y que no devuelva nada.
Si necesitamos conocer la vista que ha causado el evento, escribiríamos:
boton.setOnClickListener { vista -> hazAlgo(vista) }
Preguntas de repaso: Lambdas

Funciones en Kotlin

video[Tutorial Funciones en Kotlin

Los métodos de Java ahora se conocen como funciones y se declaran con fun:

int doble(int x) {
   return 2 * x
} 
fun doble(x: Int): Int {
   return 2 * x
} 
Los parámetros y el tipo devuelto se indican usando la notación Pascal. Es posible omitir el tipo que devuelve (también podemos indicar: Unit). Cada variable ha de tener su tipo y no podemos agrupar varias variables cuando son del mismo tipo.
void funcion(int p1, p2) {
   …
} 
fun funcion(p1: Int, p2: Int) {
   …
} 
Una mejora frente a Java es la posibilidad de indicar un valor por defecto en algún parámetro:
fun rellena(buff: ByteArray, valor: Byte = 0, long: Int = buff.size) {
   for (i in 0..long-1) buff[i] = valor
} 
La función anterior tiene tres parámetros, pero solo es obligatorio el primero. Como vemos a continuación puede ser llamada de muy diferentes formas.
var b = ByteArray(10)   //Creamos un array de bytes con 10 elementos
rellena(b, 99, 5)       //Rellena 5 elementos con 99
rellena(b, 99)          //Rellena todos elementos con 99
rellena(b)              //Rellena todos elementos con 0
rellena(b, long=5)      //Rellena 5 elementos con 0
rellena(long=5, buff=b) //Igual que el anterior 
Observa como en las dos últimas líneas podemos indicar el nombre de cada parámetro. Es especialmente útil cuando queramos aclarar el significado de cada parámetro, queramos solo indicar algunos o queramos indicarlos en un orden diferente. Como puedes comprobar, la forma de indicar los parámetros en Kotlin es muy flexible. Para conseguir un comportamiento similar en Java sería necesario escribir varias sobrecargas de la función.
Una función puede tener un número variable de argumentos del mismo tipo:
void imprime(String... cadenas) { 
   for (String cadena : cadenas) 
      System.out.println(cadena); 
}  
fun imprime(vararg cadenas:String){ 
   for (cadena in cadenas) 
      println(cadena)
} 

Si la función es muy corta podemos escribirla en una línea:

fun doble(x: Int): Int { 
   return 2*x 
} 
fun doble(x: Int) = 2*x   
En Kotlin, las funciones se pueden declarar en el nivel superior de un archivo, lo que significa que no es necesario crear una clase para crear una función, como en Java, C# o Scala.
Kotlin admite funciones locales, es decir, una función dentro de otra función:
fun dfs(graph: Graph) {
   fun dfs(current: Vertex, visited: Set) {
      …
   }
   dfs(graph.vertices[0], HashSet())
} 
Kotlin admite funciones con parámetros genéricos:
 void add(T a, Collection c){
    c.add(a);
} 
fun  add(a: T,
         c: MutableCollection) {
   c.add(a)
} 
Kotlin admite un estilo de programación funcional conocido como recursivi-dad de cola. Esto permite que algunos algoritmos que normalmente se escribi-rían usando bucles se escriban usando una función recursiva, pero sin el ries-go de desbordamiento de la pila. Cuando una función está marcada con el modificador de tailrec y cumple con la forma requerida, el compilador optimiza la recursión, dejando en su lugar una versión rápida y eficiente basada en un bucle:
tailrec fun puntoFijoCoseno (x: Double = 1.0): Double
        = if (x == Math.cos(x)) x else puntoFijoCoseno(Math.cos(x)) 
La función anterior calcula el punto fijo del coseno de forma recursiva. Toda función marcada con tailrec ha de llamarse a sí misma justo al final de la función.

Preguntas de repaso: Funciones

Estructuras de control en Kotlin

video[Tutorial Estructuras de control en Kotlin

Escribir código en Kotlin es similar a hacerlo en Java. Las principales estructuras de control (if, while, for, …) son muy parecidas. No obstante, hay algunas diferencias que pasamos a destacar.
El uso de ; es opcional al final de línea:

a = 0; b = a + 1;
c = b; 
a = 0; b = a + 1
c = b 
Como vemos en el código siguiente a la derecha, if se utiliza de igual forma que Java. Aunque Kotlin permite su uso en una expresión:
if (a > b) max = a
else       max = b 
max = if (a > b) a
      else       b
max = if (a > b) {
   print(a); a
} else {
   print(b); b
}   
Como se muestra a la derecha, Kotlin también permite una combinación de los dos estilos.
when reemplaza a switch: mejorando la legibilidad:
 
switch(x) {
  case 1:
     System.out.println("uno");
     break;
  case 2:
  case 3:
     System.out.println("2 o 3");
     break;
  default :
     System.out.println("error");
     error = true;
}
when(x) {
    1    -> print("uno")
    2, 3 -> print("2 o 3")
    else -> {
        println("error")
        error = true
    }
}  
Además, when permite su uso en expresiones:
s = when (objeto) {
   1          -> "Número uno"
   "Hola"     -> "Saludo"
   is Long    -> "Long"
   in 4..8    -> "Entre 4 y 8"
   else       -> "por defecto"
} 
n = when {
   x == 1              -> 1
   s.contains("hello") -> 2
   !x in 2..10         -> 3
   x is String         -> 5
   else                -> 6
} 
 Observa el ejemplo de la izquierda. objeto es comparado con expresiones de diferentes tipos. Además, se permite mezclar expresiones que se comparan con el valor indicado, con expresiones booleanas. Como se muestra en el ejemplo de la derecha, podemos omitir el valor a comprobar. En este caso hemos de utilizar expresiones booleanas.

Nota: Cuando when asigna un valor a una variable, es obligatorio poner un else o cubrir todos los casos posibles.

El típico bucle for desaparece. Ahora se utiliza un estilo similar a foreach de otros lenguajes o el basado en la clase Iterator de Java.
for (int i=1; i<=10; i++) {
   System.out.println(i);
} 
for (i in 1..10) {
   println("valor de i: "+i);
}    

Ahora hemos de recorrer los elementos de una colección. Una colección ha de seguir el interfaz Iterator (con las funciones next() y hasNesxt()).  Kotlin permite definir colecciones de forma sencilla como se muestra a continuación:
 

for (i in 4 downTo 1) …  //4,3,2,1
for (i in 1..9 step 2) … //1,3,6,8 
for (i in 1 until 4) … //1,2,3
for (c in "Hola") … //'H','o','l','a' 
Siguen esta interfaz los arrays, los string o las colecciones. En estos casos puede resultar útil recorrer la colección usando un índice:
for (i in array.indices) {
   print(array[i])
}  
for ((indice,valor) in array.withIndex()) {
  println("en posición $indice hay $va-lor")
}  
La estructura de control while funciona de forma convencional:
while (x > 0) {
   x--
} 
do {
   val x = obtenDatos()
while (x != null) //x es visible aquí 
Podemos salir de una estructura de control usando:
  • return : retornamos de la función actual (o función anónima).
  • break : terminamos el bucle más cercano.
  • continue : continuamos con el siguiente paso en el bucle más cercano.

Si no queremos salir de la estructura más cercana si no de otra, podemos utilizar etiquetas de salto.

loop@ for (i in 1..100) {
    for (j in 1..100) {
        if (...) break
        if (...) break@loop
    }
} 

Variables en Kotlin

video[Tutorial Variables en Kotlin

La declaración de variables utiliza una sintaxis diferente a Java. Puedes apreciar cómo es necesario utilizar la palabra reservada var.

int i = 1;
String s = "Hola";
double d; 
var i: Int = 1
var s: String = "Hola"
var d: Double 
var i = 1
var s = "Hola"
var d: Double  
Como se muestra en la tercera columna, el tipo de una variable puede ser omitido en caso de que pueda deducirse del contexto. Esto no quiere decir que una variable puede cambiar de tipo más adelante. Kotlin es un lenguaje fuertemente tipado.
Existen dos tipos de variables en Kotlin, mutables e inmutables. Para diferenciarlas iniciaremos var o val. Una variable inmutable no puede ser modificada una vez ha sido inicializada. Es decir, se comporta como una constante. Es lo mismo que utilizar final en Java. El concepto de inmutabilidad es muy interesante. Al no poder cambiar los objetos estos son más robustos y eficientes. Siempre que podamos hemos de usar objetos inmutables.
final int i = j + 1;
final String s ="Hola"; 
val i = j + 1
val s = "Hola" 
val i = 1
i = i + 1   //ERROR  
Si un valor es conocido en tiempo de compilación se pueden marcar como const:
const val PI = 3.1416 
const val MONEDA = "Euro" 
Las constantes en tiempo de compilación solo pueden declararse en el nivel superior (fuera de una clase) o como miembro de una clase. Además, solo pueden contener valores primitivos o de String.
 Los tipos primitivos de Java (int, double, char, …) han sido reemplazados por clases (Int, Double, Char, …). Internamente, estos valores se representan como lo hace Java, pero estos detalles son ocultados al programador que ha de tratarlos como si fueran clases.
En la siguiente tabla se muestra los tipos simples en Kotlin. Su representación interna coincide al 100% con los de Java, la única diferencia es que en Kotlin son tratados como objetos. Esto simplifica la programación; en Kotlin solo tendremos que tratar con un tipo de datos, los objetos. Pero internamente se pasan a tipos primitivos cuando es posible.
El tipo Unit corresponde a void en Java.
En Kotlin no hay conversión directa entre números, de modo que tendremos que usar las funciones correspondientes para cambios de tipo. Además, Char no puede ser convertido directamente a un número. 
val i: Int = 7
val d: Double = i.toDouble
val c: Char = 'c'
val i: Int = c.toInt()  
En Kotlin no es necesario realizar un typecast cuando solo hay una alternativa.
Los operadores para manipular variables coinciden en Java y Kotlin. Con excepción de los que trabajan con operaciones a nivel de bit:
int v = 0b00110100 | 0b00001111;
int v = 0b00110100 & 0b00001111;
int v = 0b00110100 ^ 0b00001111;
int v = 1 << 4;
int v = 0b10000000 >> 1;
int v = 0b10000000 >>> 1;
int v = ~ 0b10000000; 
val v = 0b00110100 or  0b00001111
val v = 0b00110100 and 0b00001111
val v = 0b00110100 xor 0b00001111
val v = 1 shl 4
val v = 0b10000000 shr 1
val v = 0b10000000 ushr 1
val v = 0b10000000.inv() 

Strings

Usa el operador + para concatenar cadenas, o incluso otros tipos siempre que el primer elemento sea una cadena:

println("La cadena " + s + " tiene " + s.length + " caracteres.")

Puedes usar el carácter $ para insertar variables o expresiones:

printn("La cadena $s tiene ${s.length} caracteres.")

Para añadir el carácter $ usa:

println("Precio en dólares ${'$'}99")
En Kotlin puedes declarar una cadena usando comillas triples. Estas cadenas pueden contener saltos de línea y tabuladores sin necesidad de usar \n, \t y \":
val cadena = "\n<HTML>\n\t\"texto\"\n</HTML>" 
 
 
 
 
val cadena ="""
<HTML>
    "texto"
</HTML>
""" 

Introducción a Kotlin

video[Tutorial Introducción a Kotlin

Un poco de historia

Kotlin fue creado en 2010 por JetBrains, empresa responsable de Intellij IDEA, el entorno de desarrollo en que se basa Android Studio. En JetBrains trabajaban habitualmente con Java, lenguaje que les resultaba muy farragoso y pesado. Para algo tan simple como asociar código a un evento, teníamos que declarar una interface e instanciar un objeto anónimo.  Para cambiar el atributo de un objeto teníamos que hacerlo a través de un setter. Parece como si los diseñadores de Java disfrutaran escribiendo cientos de líneas de código repetitivo.
En JetBrains necesitaban un lenguaje más conciso que Java, pero que compilara en la máquina virtual Java. La opción más clara era utilizar Scala, pero lo descartaron por razones de eficiencia y exceso de potencia. Puede parecer que, a más potencia mejor, pero un exceso de potencia da mucha libertad al desarrollador, que puede usar diferentes estilos para escribir código. Esto da problemas cuando trabajamos en equipo o en el testing. Por lo tanto, decidieron crear un nuevo lenguaje que fuera 100% interoperable con Java, pero que incorporara las últimas técnicas de lenguajes más modernos como Scala, C#, Go, Swift o Python.
Con respecto a la potencia y libertad que nos proporciona cada lenguaje, podemos hacer la siguiente recapitulación histórica. C++ fue creado para dar una gran libertad al programador. Este podía utilizar diferentes paradigmas y estilos de programación. Esta característica se vio contraproducente, ya que un mismo programa podría escribirse de formas muy diferentes. Java trata de corregir este exceso, creando un lenguaje muy sencillo, pero posiblemente se peque de exceso de rigurosidad. El código resulta muy pesado de escribir e incluso poco legible. Luego aparece Scala, basado en el mismo concepto de máquina virtual Java, pero dando mucha más flexibilidad y potencia al lenguaje. Finalmente aparece Kotlin, que podríamos decir que se encuentra en un término medio. Es más flexible que Java, pero menos que Scala o C++.

Características de Kotlin

Cada vez que oímos que ha aparecido un nuevo lenguaje de programación, lo primero que se nos pasa por la cabeza es: ¿No hay ya suficientes lenguajes de programación? ¿Merece la pena aprender uno nuevo? Parece que muchos desarrolladores de Android opinan que realizar este esfuerzo sí que merece la pena. En este primer punto, vamos a intentar convencerte de que no te arre-pentirás aprendiendo Kotlin. Pasamos a describir las principales características de Kotlin. Como un ejem-plo vale más que mil palabras, trataremos de demostrar cada característica comparando código Java con Kotlin.
Para ampliar información te recomendamos la referencia oficial de Kotlin kotlinlang.org/docs/reference/ y el libro Kotlin for Android Developers de Antonio Leiva.

Conciso

Lo primero que te llamará la atención es que para programar en Kotlin vas a tener que escribir mucho menos código. Veamos los casos más exagerados:
  •     Uso de Lambdas o funciones anónimas:
botón.setOnClickListener(
  new View.OnClickListener(){
    @Override
    public void onClick(View v){
       hazAlgo();
    }
}); 
boton.setOnClickListener { hazAlgo() } 
  •     Definición de clases mucho más concisa:
public class Persona {
   private String nombre;
   public String getNombre() {
      return nombre;
   }
   public void setNombre(String n){
      nombre = n;
   }
} 
class Persona {
   var nombre: String = ""
} 
  •    Clases de datos para definir POJOS.
  •    Adiós a findViewById():
TextView textView = (TextView) 
     findViewBy-Id(R.id.textView);
textView.setText("¡Hola Mun-do!"); 
textView.text = "¡Hola Mundo!" 

Legible y expresivo

Otra característica de Kotlin es que resulta mucho más legible. Al leer el código nos va a costar menos entender su significado. Veamos algunos aspectos donde se ha mejorado la expresividad:

  •    Un código conciso resulta siempre más legible que uno largo. No tienes más que comparar los códigos anteriores y ver cuál resulta más legible.
  •    Adiós a los getters y setter:
     
textView.setText("¡Hola Mun-do!"); 
textView.text = "¡Hola Mundo!" 
  •     Funciones con nombres de atributos:
format("hola", true, true); 
format("hola", normalizeCase = true,
        upperCaseFirstLetter = true);
  •    Nuevas estructuras de control for y when más expresivas.
  •    Extiende la funcionalidad de una clase sin usar la herencia de forma legible y natural.
Preguntas de repaso: Características de Kotlin.