changed 6 years ago
Published Linked with GitHub

Kotlin

Sacándole los colores a Java

Pedro Joya Máñez

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

https://hackmd.io/@pedrojoya/kotlin#/


Acerca de mí

Pedro Joya

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

  • Docente de Ciclos Formativos de Formación Profesional de Informática en el IES Saladillo de Algeciras
  • Profesor de desarrollo de aplicaciones en Android
  • Apasionado de Kotlin

twitter: @pedrojoyamanez


¿Qué es Kotlin?

  • Lenguaje de programación creado por JetBrains
  • Inspirado en otros lenguajes: Java, C#, Scala, Groovy, Ruby
  • Podemos usarlo para desarrollo mobile, web front-end y back-end (Spring, Android, web, nativo)

¿Por qué Kotlin?

  • Sintaxis simple y concisa
  • Curva de aprendizaje suave
  • Interoperable con Java. Compila hacia JVM bytecode
  • Lenguaje moderno, conciso y con características de lenguajes funcionales
  • Google lo ha elegido como lenguaje prioritario para programación en Android


Show me the code


El ; es opcional

// JAVA String name = "Baldomero";
// KOTLIN val name: String = "Baldomero"
  • En Kotlin el ; es opcional, excepto en un caso específico de los enums
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →

val vs. var

// JAVA final String name = "Baldomero"; int age = 45;
// KOTLIN val name: String = "Baldomero" var age: Int = 45
  • val define una variable que no puede referenciar ningún otro objeto en el futuro.
  • var define una varible que puede referenciar distintos objetos a lo largo de su vida

Inferencia de tipos

// JAVA <10 final String name = "Baldomero"; // JAVA 10 var name = "Baldomero";
// KOTLIN var name = "Baldomero"
  • Tipo inferido (deducido) a partir del tipo de la expresión de inicialización
  • Lenguaje fuertemente tipado de tipo estático, aunque haya sido inferido (tiempo de compilación)

Tipos referenciales, no primitivos

// KOTLIN val value: Int = 146 val percent = value.coerceIn(0, 100)
  • No exiten los tipos primitivos, todos son clases.
  • Internamente se usará un tipo primitivo cuando sea posible, con boxing y unboxing automático
  • No hay conversión automática de tipos numéricos, sino funciones de conversión explícita en los tipos, como toLong(), toInt(), etc.

Template Strings (interpolación)

// JAVA final String s = "abc"; System.out.println(s + ".length is " + s.length());
// KOTLIN val s = "abc" println("$s.length is ${s.length}")
  • $variable para interpolar el valor de una variable
  • ${expresion} para interpolar el valor de cualquier expresión

Raw Strings

// JAVA final String artist = "Chiquito de la Calzada"; final String saying = "No te digo trigo...\n" + "por no llamarte Rodrigo\n" + "(" + artist + ")";
// KOTLIN val artist = "Chiquito de la Calzada" val saying = """ |No te digo trigo |por no llamarte Rodrigo |($artist) """.trimMargin()
  • Literal de cadena sin caracteres de escape (\n, \t, )
  • Puede contener interpolación.
  • Se propuso para Java 12, pero fue pospuesto.

if else es una expresión

// KOTLIN val max = if (a > b) { print("a es el máximo") a } else { print("b es el máximo") b }
  • Las estructura if else es una expresión, que se evalúa al valor al que se evalúa la rama ejecutada
  • Una rama se evalúa al valor al que se evalúa la última expresión de la rama

No hay operador ternario

// JAVA final int max = a > b ? a : b;
// KOTLIN val max = if (a > b) a else b
  • En este caso Kotlin puede resultar más verboso que Java
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →

Estructura for

// JAVA for (int i = 1; i <= 10; i++) System.out.println(i);
// KOTLIN for (i in 1..10) println(i)
  • Kotlin no posee la estructura for tradicional de Java
  • Sólo for sobre iterador (for each)
  • Podemos usar un literal de rango de valores para iterar sobre él

Range

// JAVA for (int i = 1; i < 10; i++) System.out.println(i); for (int i = 6; i >= 0; i-=2) System.out.println(i);
// KOTLIN for (i in 1 until 10) println(i) for (i in 6 downTo 0 step 2) println(i)
  • La clase Range modela un rango de datos y posee un iterador
  • Podemos usar los métodos until, downTo y step para crear el rango adecuado

when como switch mejorado

when (input) { 1 -> println("Uno") 7, 8 -> println("Siete u ocho") in 10..19 -> println("Decena") is String -> println("Cadena de ${input.length}") else -> { println("Otra cosa") println("Lo sentimos") } }
  • No necesita break
  • Más de un valor de comparación en cada rama
  • Comprobación de pertenencia o no a un rango, array o lista
  • Comprobación de pertenencia o no a un tipo

when es una expresión

val semester = when (month) { in 1..6 -> "Primer semestre" in 7..12 -> "segundo semestre" else -> "Mes no válido" }
  • Devuelve el valor al que se evalúa la rama seleccionada, que corresponde al valor al que se evalúa su última expresión
  • Debe ser exhaustivo (abarcar todas las posibilidades)
  • Java 12 incorpora una versión preliminar de switch mejorado con funcionalidad similar

when como if else if

// JAVA if (x.isOdd()) System.out.println("x es impar"); else if (x.isEven()) System.out.println("x es par"); else System.out.println("x está indecisa");
// KOTLIN when { x.isOdd() -> println("x is impar") x.isEven() -> println("x is par") else -> println("x está indecisa") }
  • Las ramas se consideran expresiones booleanas y se ejecutará la primera que sea verdadera
  • También puede usarse como expresión

Hydration break


Definición de funciones

// JAVA String greet(String name, String message) { return message + ", " + name; }
// KOTLIN fun greet(name: String, message: String): String { return "$message, $name" }
  • fun is
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
  • El tipo de retorno se coloca al final, pero puede ser inferido

Funciones que no retornan "nada"

// JAVA void greet(String name, String message) { System.out.println(message + ", " + name); }
// KOTLIN fun greet(name: String, message: String) { println("$message, $name") }
  • El tipo de retorno es Unit, pero podemos omitirlo
  • Unit es un tipo, no una palabra reserva como void
  • La función retorna en realidad el objeto Unit (singleton)

Funciones con una única expresión

// JAVA String greet(String name, String message) { return message + ", " + name; }
// KOTLIN fun greet(name: String, message: String) = "$message, $name"
  • El tipo de retorno es inferido a partir del tipo de la expresión, aunque puede ser especificado explícitamente

Argumentos con valores por defecto

// JAVA String greet(String name) { return greet(name, "Hola"); } String greet(String name, String message) { return message + ", " + name; }
// KOTLIN fun greet(name: String, message: String = "Hola") = "$message, $name"
  • Permiten mejorar una API existente fácilmente
  • El valor por defecto de un parámetro puede usar el valor de un argumento anterior
  • En Java se simulan mediante sobrecarga de métodos

Named arguments

// KOTLIN fun greet(name: String = "Baldomero", message: String = "Hola") = "$message, $name" println(greet(message="Quillo que")) println(greet(message="Quillo que", name="Germán Ginés")) println(greet("Germán Ginés", message="Quillo que"))
  • Permite especificar sólo algunos argumentos
  • Permite cambiar el orden de los argumentos
  • Puede usarse en conjunción con el paso de argumentos posicional, empezando por éste
  • Esta funcionalidad NO existe en Java

varargs y spread operator

// KOTLIN fun printStrings(vararg strings: String) { for (string in strings) println(string) } val names = arrayOf("Baldomero", "Germán Ginés") printStrings("Quillo que", *names, "Na aquí")
  • El operador * (operador de dispersión) delante de un array retorna la lista de valores del array separados por coma ,

Top level functions and properties

// KOTLIN // Directamente en un fichero, fuera de cualquier clase const val PI = 3.14 var isUserLoggedIn: Boolean = false fun greet(name: String) { println("Hola $name") } fun main() { greet("Baldomero") isUserLoggedIn = true println("El número PI vale $PI") }
  • Funciones fuera de cualquier clase. Ejemplo: main
  • Propiedades fuera de cualquier clase.
  • const para constantes conocidas en tiempo de compilación, que el compilador usará de modo inline

Extension functions

// JAVA public static String shout(String receiver) { return "¡" + receiver.toUpperCase() + "!"; } String greet = "Quillo que"; System.out.println(StringsUtils.shout(greet));
// KOTLIN fun String.shout(): String = "¡${this.toUpperCase()}!" val greet = "Quillo que" println(greet.shout())
  • Llamada como si fuera un miembro de una determinada clase.
  • El IDE sugiere la función al escribir un . tras una variable de dicho tipo
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
  • Dentro de la función, this corresponde al receptor

Lambdas

// KOTLIN fun operateAndPrint(x: Int = 0, action: (Int) -> Int): Int { val result = action(x) // action.invoke(x) println("Result: $result") return result } val increm3 = { x: Int -> x + 3 } operateAndPrint(2, increm3)
  • Tipos función (vs. interf. func. de Java)
  • Expresión lambda = literal de objeto función, almacenable y pasable como argumento
  • Una lamba se ejecuta mediante el operador () o llamando a su método invoke()
  • La expresión lambda se evalúa al valor al que se evalúe la última expresión de su cuerpo

Lambdas como argumento

// KOTLIN fun operateAndPrint(x: Int = 0, action: (Int) -> Int): Int { val result = action(x) println("Result: $result") return result } operateAndPrint(2, { x -> x + 3}) operateAndPrint(2) { x -> x + 3} operateAndPrint { x -> x + 3 } operateAndPrint { it + 3 }
  • Inferencia de tipos en los parámetros de la lambda
  • Si lambda es último argumento, sacar de los ()
  • Si lambda es único argumento, nos ahorramos los ()
  • Si lambda tiene un solo argumento, usar it en cuerpo

Smart cast

// JAVA if (obj instanceof String) System.out.println(((String) obj).length());
// KOTLIN if (obj is String) println(obj.length)
  • El compilador hace el cast internamente dentro de la rama
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
  • Sólo puede aplicarse si la variable no puede cambiar entre la comprobación y el acceso
  • También en ramas de la estructura when y en comprobación de no null

Tipos nullable y no nullables

// KOTLIN var name: String? = "Baldomero" var sirname: String = "Llegate Ligero" name = null if (name != null) println(name.length)
  • Nulabilidad dentro del sistema de tipos.
  • Detección de errores relacionados con null en tiempo de compilación
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
  • Tipo? permite null, Tipo no permite null. Tipo es un subtipo de Tipo?
  • Smart cast de Tipo? a Tipo tras comprobación de que la variable no es null

Jerarquía de tipos

  • Any es similar a Object
  • Cada tipo no nullable es subtipo del correspondiente nullable
  • Nothing es subtipo de todos los tipos

Nothing y throw como expresión

// KOTLIN fun fail(message: String): Nothing { throw IllegalStateException(message) } val fruitPerGlass: Int = if (glassesSold > 0) orangesUsed / glassesSold else throw NoGlassesSold()
  • El tipo Nothing indica que la función nunca terminará satisfactoriamente y por tanto no retornará
  • Es subtipo de todos los tipos
  • throw es una expresión que retorna Nothing

Operador de acceso seguro

// JAVA final Integer length = nickname != null ? nickname.length() : null; final String name = nickname != null ? nickname.toUpperCase() : null;
// KOTLIN val length: Int? = nickname?.length val name = nickname?.toUpperCase()
  • No se permite usar el operador . con variables de tipo nullable (acceso seguro)
  • Se debe usar ?. en vez de . para acceder a propiedades y métodos
  • Se evalúa a null si la variable contiene null

Operador elvis

// JAVA final int length = nickname != null ? nickname.length() : 0;
// KOTLIN val length: Int = nickname?.length ?: 0
  • Devuelve la expresión de la izquierda si ésta es distinta de null y la de la derecha en caso contrario
  • Funciona como un valor por defecto si la expresión es null

Operador bang bang

// JAVA String name = "Baldomero"; final Integer length = name.length();
// KOTLIN var name: String? = "Baldomero" val lenght = name!!.length
  • Produce NullPointerException si la variable es null
  • Se recomienda sólo usarlo cuando estemos completamente seguros de que no es null.

let

// JAVA final String name = "Baldomero"; if (name != null) { System.out.println("Name: " + name); }
// KOTLIN val name: String? = "Baldomero" name?.let { println("Name: $it") }
  • Extension function, sobre cualquier tipo, que recibe como parámetro una expresión lambda
  • La lambda es ejecutada pasándole como argumento el objeto sobre el que se ejecuta let
  • Retorna el valor retornado por la expresión lambda.

also

// JAVA final Student student = new Student(); student.setAge(45); student.setAddress("c/ Avda. Duque de Rivas, 1")
// KOTLIN val student: Student = Student().also { it.age = 45 it.address = "c/ Avda. Duque de Rivas, 1" }
  • Similar a let pero retorna el propio objeto sobre el que se ejecuta also
  • Se puede usar para configurar un objeto al crearlo antes de retornarlo (patrón builder)

Hydration break


Constructor primario

// JAVA final class Student extends Person { private final boolean isRepeater; private int age; public Student(@NotNull String name, boolean isRepeater, int age) { super(name); this.isRepeater = isRepeater; this.age = age; } }
// KOTLIN class Student(name: String, val isRepeater: Boolean, var age) : Person(name)
  • Constructor primario definido en primera linea
  • Llamada al constructor primario de la superclase al definir la herencia

Bloque de inicialización

// JAVA final class Student extends Person { private final boolean isRepeater; private int age; public Student(@NotNull String name, boolean isRepeater, int age) { super(name); this.isRepeater = isRepeater; this.age = age; // Código de inicialización... } }
// KOTLIN class Student(name: String, val isRepeater: Boolean, var age: Int) : Person(name) { init { // Código de inicialización... } }
  • Código de inicialización en bloque init.

Instanciación

// JAVA final Student student = new Student("Baldomero", false, 23);
// KOTLIN val student = Student("Baldomero", false, 23)
  • No existe el operador new

Concepto de propiedad

// JAVA final class Student extends Person { private final boolean isRepeater; private int age; private int grade; public Student(@NotNull String name, boolean isRepeater, int age) { super(name); this.isRepeater = isRepeater; this.age = age; } public boolean isRepeater() { return isRepeater; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getGrade() { return grade; } public void setGrade(int grade) { this.grade = grade; } }
  • Al conjunto formado por el campo (field), el getter y el setter se le conoce como propiedad

Propiedades en Kotlin

// KOTLIN class Student(name: String, val isRepeater: Boolean, var age: Int) : Person(name) { var grade: Int = 0 }
  • Definidas directamente en el constructor primario o en el cuerpo de la clase
  • Los parámetros del constructor sin val ni var NO crean propiedades. Son valores para inicialización de otras propiedades

Getter y setters personalizados

// KOTLIN class Student(name: String, val isRepeater: Boolean, var age: Int) : Person(name) { var grade: Int = 0 set(value) { if (value >= 0) field = value else throw IllegalArgumentException() } val gradeDescription: String get() = when(grade) { in 0..4 -> "Mal" in 5..7 -> "Aceptable" in 8..10 -> "Bien" else -> "No válida" } }
  • field representa el campo interno creado
  • Podemos crear propiedades que no tienen ningún campo asociado

Acceso a propiedades

// JAVA final Student student = new Student("Baldomero", false, 25); System.out.println(student.getName()); student.setGrade(8); System.out.println(student.getGradeDescription());
// KOTLIN val student = Student("Baldomero", false, 25) println(student.name) student.grade = 8 println(student.gradeDescription)
  • Se usa el operador . para acceder a una propiedad, tanto para lectura como para escritura.

Notación infix

// JAVA public final class Pizza { public final void add(@NotNull String ingredient) { System.out.println(ingredient + " added to pizza"); } } Pizza pizza = new Pizza(); pizza.add("Cheese");
// KOTLIN class Pizza { infix fun add(ingredient: String) { println("$ingredient added to pizza") } } val pizza = Pizza() pizza add "Cheese"
  • Notación de operaciones aritméticas binarias
  • Método o extension function con un único parámetro

Concepto de data class

// JAVA public final class User { @NotNull private final String name; private final int age; public User(@NotNull String name, int age) { ... } @NotNull public final String getName() { ... } public final int getAge() { ... } @NotNull public String toString() { ... } public int hashCode() { ... } public boolean equals(@Nullable Object var1) { ... } // ... }
  • Clase cuyo propósito principal es contener datos. Habitualmente implementan equals(), hashCode() y toString().

Data class en Kotlin

data class User(val name: String, val age: Int)
  • Implementaciones por defecto del constructor, getters de propiedades, setters para propiedades var, toString(), hashCode(), equals()
  • Se puede implementar explícitamente toString(), hashCode() y equals() en el cuerpo de la clase
  • Implementación del método copy() para crear un nuevo objeto a partir de uno existente
  • Implementación de métodos para desestructuración: component1(), component2(), etc.

Desestructuración

User jane = new User("Jane", 35); String name = jane.component1(); int age = jane.component2();
val jane = User("Jane", 35) val (name, age) = jane
  • Asignación a lista de variables individuales, desde propiedades de un objeto (o elementos de colección)

Delegación

interface Base { fun print() } // Cuando se llame al método print() sobre un objeto de la clase // Derived se llamará automáticamente al método print() de objeto b. class Derived(b: Base) : Base by b { // Otros métodos }
  • Delegación y composición vs. herencia
  • El compilador generará automáticamente en la clase Derived el método print(), que internamente llamará a b.print()

Object

public final class RepositoryImp implements Repository { public static final RepositoryImp INSTANCE = new RepositoryImp(); private RepositoryImp() { } // ... } RepositoryImp repository = RepositoryImp.INSTANCE;
object RepositoryImp : Repository { // ... } val repository = RepositoryImp.INSTANCE
  • Patrón singleton en modo eager con solo usar object en vez de class

Sealed classes

sealed class Post // Las clases hijas pueden ser clases normales, data classes e incluso objects. data class Status(var text: String) : Post() data class Image(var url: String, var caption: String) : Post() data class Video(var url: String, var timeDuration: Int, var encoding: String): Post() object Headline : Post()
  • Clase que sólo puede tener unas determinadas clases hijas predefinidas
  • Es abstracta por definición
  • Tanto la clase padres como las hijas deben estar definidas en el mismo fichero

No exite la palabra reservada static

// JAVA class Person { public static void callMe() { } } Person.callMe();
// KOTLIN class Person { companion object { fun callMe() { } } } Person.callMe()
  • Clases de utilidad no necesarias (extension functions y top level functions)
  • Companion object asociado a una clase

Sobrecarga de operadores

// KOTLIN data class Point(var x: Double, var y: Double) { operator fun plus(point: Point) = Point(x + point.x, y + point.y) } val p1 = Point(2.9, 5.0) val p2 = Point(2.0, 7.5) val p3 = p1 + p2
  • Conjunto predefinido de operadores con representación simbólica; +, - ,
  • Al usar un operador internamente se llama al método asociado a él: a + b se traduce a a.plus(b)
  • Podemos definir el método asociado a un determinado operador en nuestras clases

Asociaciones de operadores

Expresión Se traduce a
a + b, a - b, a * b a.plus(b), a.minus(b), a.times(b)
a..b, a in b a.rangeTo(b), b.contains(a)
a[i], a[i] = b a.get(i), a.set(i, b)
a() a.invoke()
a == b a?.equals(b) ?: (b === null)
a > b a.compareTo(b) > 0
a += b a.plusAssign(b)

Colecciones mutables e inmutables

// KOTLIN val inmutableList = listOf(1, 2, 3) val mutableList = mutableListOf(1, 2, 3) mutableList.add(4)
  • Interfaces distintas para colecciones inmutables y mutables

Trabajo funcional con colecciones

// JAVA 9 List.of(4, -1, 2, -8) .stream() .filter(it -> it > 0) .map(it -> it * 2) .forEach(System.out::println);
// KOTLIN listOf(4, -1, 2, -8) .filter { it > 0 } .map { it * 2 } .forEach(::println)
  • Métodos de filtrado, transformación, reducción, etc. directamente en las colecciones
  • Por defecto eager aunque puede convertirse en lazy
  • lambdas y referencias a métodos

Otros cambios

  • Las clases y los métodos son públicos por defecto
  • Las clases y los métodos son final por defecto (usar open para abrirlos)
  • Las clases internas son static por defecto
  • protected es más restrictivo: sólo subclases
  • Modificador de acceso internal para visibilidad a nivel de módulo
  • Un fichero puede tener más de una clase pública
  • No existen las checked exceptions. Tampoco throws

En resumen



Referencias


Select a repo