# Kotlin ## Sacándole los colores a Java Pedro Joya Máñez ![](https://i.imgur.com/Vc5xLpv.png) https://hackmd.io/@pedrojoya/kotlin#/ --- ### Acerca de mí #### Pedro Joya :man: - 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](https://twitter.com/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 --- ![](https://i.imgur.com/Alm8ETd.jpg) --- ### Show me the code ![](https://media.giphy.com/media/AOSwwqVjNZlDO/giphy.gif) --- ### El ; es opcional ```java= // JAVA String name = "Baldomero"; ``` ```kotlin= // KOTLIN val name: String = "Baldomero" ``` * En Kotlin el ; es opcional, excepto en un caso específico de los enums :scream: :clap: :dancer: --- ### val vs. var ```java= // JAVA final String name = "Baldomero"; int age = 45; ``` ```kotlin= // 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= // JAVA <10 final String name = "Baldomero"; // JAVA 10 var name = "Baldomero"; ``` ```kotlin= // 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= // 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= // JAVA final String s = "abc"; System.out.println(s + ".length is " + s.length()); ``` ```kotlin= // 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= // JAVA final String artist = "Chiquito de la Calzada"; final String saying = "No te digo trigo...\n" + "por no llamarte Rodrigo\n" + "(" + artist + ")"; ``` ```kotlin= // 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= // 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= // JAVA final int max = a > b ? a : b; ``` ```kotlin= // KOTLIN val max = if (a > b) a else b ``` - En este caso Kotlin puede resultar más verboso que Java :confused: --- ### Estructura for ```java= // JAVA for (int i = 1; i <= 10; i++) System.out.println(i); ``` ```kotlin= // 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= // 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= // 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 ```kotlin= 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 ```kotlin= 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= // 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= // 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 ![](https://media.giphy.com/media/dlbcP3ngz7c6j1kOW2/giphy.gif) --- ### Definición de funciones ```java= // JAVA String greet(String name, String message) { return message + ", " + name; } ``` ```kotlin= // KOTLIN fun greet(name: String, message: String): String { return "$message, $name" } ``` * fun is :smile: * El tipo de retorno se coloca al final, pero puede ser inferido --- ### Funciones que no retornan "nada" ```java= // JAVA void greet(String name, String message) { System.out.println(message + ", " + name); } ``` ```kotlin= // 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= // JAVA String greet(String name, String message) { return message + ", " + name; } ``` ```kotlin= // 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= // JAVA String greet(String name) { return greet(name, "Hola"); } String greet(String name, String message) { return message + ", " + name; } ``` ```kotlin= // 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= // 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= // 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= // 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= // JAVA public static String shout(String receiver) { return "¡" + receiver.toUpperCase() + "!"; } String greet = "Quillo que"; System.out.println(StringsUtils.shout(greet)); ``` ```kotlin= // 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 :dancer: - Dentro de la función, this corresponde al receptor --- ### Lambdas ```kotlin= // 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= // 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= // JAVA if (obj instanceof String) System.out.println(((String) obj).length()); ``` ```kotlin= // KOTLIN if (obj is String) println(obj.length) ``` - El compilador hace el cast internamente dentro de la rama :bulb: - 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= // 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 :clap: - 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 ![](https://i.imgur.com/K4HzWGD.png) - 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= // 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= // JAVA final Integer length = nickname != null ? nickname.length() : null; final String name = nickname != null ? nickname.toUpperCase() : null; ``` ```kotlin= // 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= // JAVA final int length = nickname != null ? nickname.length() : 0; ``` ```kotlin= // 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= // JAVA String name = "Baldomero"; final Integer length = name.length(); ``` ```kotlin= // 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= // JAVA final String name = "Baldomero"; if (name != null) { System.out.println("Name: " + name); } ``` ```kotlin= // 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= // JAVA final Student student = new Student(); student.setAge(45); student.setAddress("c/ Avda. Duque de Rivas, 1") ``` ```kotlin= // 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 ![](https://media.giphy.com/media/dlbcP3ngz7c6j1kOW2/giphy.gif) --- ### Constructor primario ```java= // 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= // 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= // 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= // 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= // JAVA final Student student = new Student("Baldomero", false, 23); ``` ```kotlin= // KOTLIN val student = Student("Baldomero", false, 23) ``` - No existe el operador new --- ### Concepto de propiedad ```java= // 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= // 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= // 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= // JAVA final Student student = new Student("Baldomero", false, 25); System.out.println(student.getName()); student.setGrade(8); System.out.println(student.getGradeDescription()); ``` ```kotlin= // 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= // 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= // 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= // 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 ```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 ```java= User jane = new User("Jane", 35); String name = jane.component1(); int age = jane.component2(); ``` ```kotlin= 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 ```kotlin= 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 ```java= public final class RepositoryImp implements Repository { public static final RepositoryImp INSTANCE = new RepositoryImp(); private RepositoryImp() { } // ... } RepositoryImp repository = RepositoryImp.INSTANCE; ``` ```kotlin= object RepositoryImp : Repository { // ... } val repository = RepositoryImp.INSTANCE ``` - Patrón singleton en modo eager con solo usar object en vez de class --- ### Sealed classes ```kotlin= 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= // JAVA class Person { public static void callMe() { } } Person.callMe(); ``` ```kotlin= // 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= // 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= // 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= // JAVA 9 List.of(4, -1, 2, -8) .stream() .filter(it -> it > 0) .map(it -> it * 2) .forEach(System.out::println); ``` ```kotlin= // 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... --- ![](https://i.imgur.com/vRZGsFJ.jpg) --- ### Referencias - Web oficial: [https://kotlinlang.org/](https://kotlinlang.org/) - Playground: [https://play.kotlinlang.org](https://play.kotlinlang.org) - Learn Kotlin: [https://kotlinlang.org/docs/reference/](https://kotlinlang.org/docs/reference/) --- ![](https://i.imgur.com/J9Etqb4.jpg)
{"metaMigratedAt":"2023-06-14T22:02:19.514Z","metaMigratedFrom":"YAML","title":"Kotlin sacándole los colores a Java","breaks":true,"description":"Presentación sobre Kotlin en comparación con Java","slideOptions":"{\"theme\":\"beige\",\"transition\":\"slide\",\"mouseWheel\":true,\"touch\":true,\"controlsLayout\":\"edges\",\"hideAddressBar\":true,\"keyboard\":true,\"overview\":false}","contributors":"[{\"id\":\"b3c7cbd8-c6bb-44e9-8380-34b896c77ea7\",\"add\":42685,\"del\":20618}]"}
    1179 views