{"id":23701174,"url":"https://github.com/gitfrandu4/learning-kotlin","last_synced_at":"2026-01-29T10:30:16.162Z","repository":{"id":211625164,"uuid":"544063328","full_name":"gitfrandu4/learning-kotlin","owner":"gitfrandu4","description":"Introducción al desarrollo de apps Android con Kotlin","archived":false,"fork":false,"pushed_at":"2022-10-16T18:40:20.000Z","size":2605,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-12-30T09:32:46.602Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gitfrandu4.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2022-10-01T14:47:21.000Z","updated_at":"2022-10-03T09:31:11.000Z","dependencies_parsed_at":"2023-12-09T19:52:43.946Z","dependency_job_id":null,"html_url":"https://github.com/gitfrandu4/learning-kotlin","commit_stats":null,"previous_names":["gitfrandu4/learning-kotlin"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gitfrandu4%2Flearning-kotlin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gitfrandu4%2Flearning-kotlin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gitfrandu4%2Flearning-kotlin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gitfrandu4%2Flearning-kotlin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gitfrandu4","download_url":"https://codeload.github.com/gitfrandu4/learning-kotlin/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239776640,"owners_count":19695146,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-30T09:32:28.377Z","updated_at":"2026-01-29T10:30:16.097Z","avatar_url":"https://github.com/gitfrandu4.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# M1. Introducción a Kotlin\n\n* [0. Introducción POO](#0-introducción-poo)\n* [1. Herencia en Kotlin](#1-herencia-en-kotlin)\n* [2. Operadores](#2-operadores)\n* [3. Convenciones de código](#3-convención-de-nombres)\n* [4. Herencia](#4-el-concepto-de-herencia)\n* [5. Encapsulación](#5-la-encapsulación)\n* [6. El concepto de null](#6-el-concepto-de-null)\n* [7. Funciones de ámbito](#7-funciones-de-ámbito)\n  * [Objeto del contexto](#objeto-del-contexto)\n  * [`let`](#let-1)\n  * [`with`](#with-1)\n  * [`run`](#run-1)\n  * [`apply`](#apply-1)\n  * [`also`](#also-1)\n\n## 0. Introducción POO\n\n### Clase con propiedades con valor por defecto\n\n```kotlin\nclass Animal {\n    var nombre = \"Toby\"\n    var numeroDePatas = \"4\"\n    var color = \"negro\"\n}\n```\n\n### Instanciación y acceso a propiedades\n\n```kotlin\nfun main() {\n    \n    var miAnimal = Animal() // Instancia\n    \n    println(miAnimal.nombre) // Muestra: Toby\n    println(miAnimal.numeroDePatas) // Muestra: 4\n    println(miAnimal.color) // Muestra: negro\n}\n```\n\n### Constructor primario\n\n```kotlin\nclass Animal(val nombre: String, val numeroDePatas: Int, val color: String)\n```\n```kotlin\nfun main() {\n    var miAnimal = Animal(\"Rocky\", 4, \"marrón\") // Instancia\n        \n    println(miAnimal.nombre) // Muestra: Rocky\n    println(miAnimal.numeroDePatas) // Muestra: 4\n    println(miAnimal.color) // Muestra: marrón\n}\n```\n\n### Funciones miembro\n\n```kotlin\nclass Animal(val nombre: String, val numeroDePatas: Int, val color: String) {\n    fun come() {\n        // Implementación de función\n    }\n}\n```\n\n```kotlin\nfun main() {\n    var miAnimal = Animal(\"Rocky\", 4, \"marrón\") // Instancia\n    miAnimal.come() // Llamada a la función miembro\n}\n```\n\n### Clases de datos\n\n```kotlin\ndata class Animal(val nombre: String, val numeroDePatas: Int, val color: String)\n```\n\n### Enums\n\n```kotlin\nenum class Address {\n    NORTE, SUR, ESTE, OESTE\n}\n```\n\n--- \n\n## 1. Herencia en Kotlin\n\n### Clase derivada sin constructor\n\n```kotlin\nopen class Vehiculo // La clase Vehículo permite ser extendida\n\nclass Coche : Vehiculo() // Coche extiende Vehiculo\n```\n\n### Clase derivada con constructor primario\n\n```kotlin\n// Clase abierta con constructor primario\nopen class Vehiculo(numeroDeRuedas: Int) \n```\n\n* Si la clase derivada tiene constructor primario debemos llamar al constructor de la clase base en la declaración:\n\n```kotlin\nclass Coche(numeroDeRuedas: Int) : Vehiculo(numeroDeRuedas)\n```\n\n### Sobreescritura\n\n```kotlin\nopen class Forma {\n    // La función dibujar está abierta\n    open fun dibujar() { /*...*/ } \n    \n    fun fill() { /*...*/ }\n}\n\nclass Circulo() : Forma() {\n    // En Circulo la sobreescribimos\n    override fun dibujar() { /*...*/ } \n}\n```\n\n### Clases abstractas\n\n```kotlin\n// Una clase abstracta no se puede instanciar\nabstract class Poligono { \n    \n    // Una función abstracta se debe sobreescribir\n    abstract fun dibujar() \n}\n```\n```kotlin\nclass Rectangle : Poligono() {\n    override fun dibujar() { /*...*/ }\n}\n```\n\n### Interfaces\n\n```kotlin\ninterface ManagerDeUsuario {\n    fun cambiarContrasena()\n    \n    // Las funciones se pueden implementar por defecto\n    fun obtenerNombreDeUsuario() { /*...*/ }\n}\n```\n\n### Modificadores de visibilidad para miembros de una clase\n\n* `private`: significa que es visible dentro de esta clase\n* `protected`: significa que es visible dentro de la clase y de las subclases.\n* `internal`: significa que cualquiera dentro del módulo de su misma clase puede verlo.\n* `public`: significa visible por cualquiera.\n\n---\n\n## 2. Operadores\n\n### Null\n\n```kotlin\n// La variable 'a' puede contener un String y no puede contener null\nvar a: String = \"abc\"\n\n// La variable 'b' puede contener un String o puede contener null\nvar b: String? = \"abc\"\n```\n\n```kotlin\n// Para acceder a las propiedades de b usaremos una llamada segura\nb?.lenght\n\n// Para llamar a las funciones miembro de b usaremos una llamada segura\nb?.contains('a') \n```\n\n### El operador Elvis\n\n```kotlin\nvar a: String = \"abc\"\nvar b: String? = \"abc\"\n\n// Asignamos a a el valor de b a no ser que esta sea null, en tal caso le asignamos un String vacío\na = b ?: \"\" \n```\n\n### El operador !!\n\n```kotlin\n// Intentaremos no usarlo nunca\n\n// Si b es null lanzará NullPointerException\nval longitud = b!!.length \n```\n\n### Funciones de ámbito\n\n#### Let\n\n```kotlin\nval str: String? = \"Hello\"\nval length = str?.let {\nprintln(it) // Solo se ejecuta si str no es null\n}\n```\n\n#### With\n\n```kotlin\nval numbers = mutableListOf(\"uno\", \"dos\", \"tres\")\n\nwith(numeros) {\n    println(\"'with' es llamado con el argumento $this\")\n    println(\"Contiene $size elementos\")\n}\n```\n\n#### Run\n\n```kotlin\nval servicio = MultiportService(\"https://example.kotlinlang.org\", 80)\n\nval resultado = servicio.run {\n    port = 8080\n    query(prepareRequest() + \" to port $port\")\n}\n```\n\n#### Apply\n\n```kotlin\nval adam = Persona(\"Adam\").apply {\n    edad = 32\n    ciudad = \"Madrid\"\n}\n\nprintln(adam)\n```\n\n#### Also\n\n```kotlin\nval numeros = mutableListOf(\"uno\", \"dos\", \"tres\")\n\nnumeros\n    .also { println(\"La lista de elementos hasta ahora: $it\") }\n    .add(\"cuatro\")\n```\n\n#### Take-if y take-unless\n\n```kotlin\nval numero = Random.nextInt(100)\nval parONull = numero.takeIf { it % 2 == 0 }\nval imparONull = numero.takeUnless { it % 2 == 0 }\n```\n\n--- \n\n## 3. Convención de nombres\n\n### Paquetes\n\n* Minúsculas y sin guiones bajos\n* Como estos nombres deben ser únicos, se recomienda empezar con tu dominio de internet, en orden reverso: `com.frandu4`\n* Seguido iría el nombre que identifica la finalidad del paquete\n    * ``com.mparrastia.conexion``\n* Se recomienda no usar más de una palabra, y si no queda más remedio se recomienda utilizar camel case: `com.mparrastia.miProyecto`\n\n### Clases y objetos\n\n* Para los nombres utilizamos: UpperCamelCase\n\n```kotlin\nopen class ProcesadorDeDeclaraciones { ... }\n```\n\n````kotlin\nobject ProcesadorDeDeclaracionesVacio: ProcesadorDeDeclaraciones() { ... }\n````\n\n### Funciones y variables locales\n\n* lowerCamelCase\n* Sin guiones bajos\n    * `fun procesarDeclaraciones(){ ... } `\n\n### Propiedades\n\n* Constantes: `const val NUMERO_MAXIMO = 8`\n* `val coleccionMutable: MutableSet\u003cString\u003e = HashSet()`\n    * Un objeto de tipo singleton es un objeto que es único para todo el proyecto, y podemos utilizar la misma convención que para los objetos.\n* En caso de implementar un singleton podemos usar UpperCase:\n    * `val ComparadorDePersonas: Comparator\u003cPerson\u003e = /* ... */`\n\n### Buenos nombres\n\n* Nombres de las clases: nombres que explican lo que la clase es: Lista, LectorDePdf\n* Nombres de funciones: verbos/frases que explican lo que la función hace\n* Si utilizamos acrónimos: IOStream\n* LectorDePdf (más de tres letras)\n\n---\n\n## 4. El concepto de Herencia\n\nEs un concepto importante de la POO que nos ayudará a no duplicar código y a reutilizar funcionalidades que ya hemos implementado.\n\n* La herencia es un **mecanismo para basar una clase en otra**, manteniendo su funcionalidad.\n* Otra forma de definirlo es que nos permite **derivar nuevas clases de otra existente**\n* A la clase base se le llama **Superclase** y a las derivadas **Subclase**. `Superclase -\u003e Subclase`\n    * La `Subclase` adquiere las propiedades y las funcionalidades de la `Superclase`\n\n### Jerarquías\n\nMediante la herencia vamos creando **jerarquías**.\n\nPor ejemplo:\n\n\u003cimg width=\"400px\" alt=\"example of inheritance with animals\" src=\"./docs/images/inheritance.png\" title=\"inheritance\" /\u003e\n\nEn Kotlin **la herencia es simple**, lo que quiere decir que una Subclase solo puede extender de una única Superclase\n\nEn cambio, la herencia es multinivel, por lo que una Subclase como Ave puede tener otras subclases como Loro o Ave.\n\n### Problemas de la herencia\n\nSi abusamos de la herencia podemos tener problemas de **acoplamiento**, esto quiere decir que \u003cu\u003elas Subclases quedan\natadas al destino de las Superclases para siempre\u003c/u\u003e. Una vez se ha creado una Subclase que depende de una clase base\nserá muy difícil hacer modificaciones a la clase base, ya que los efectos se propagarán descontroladamente. Así, debemos\ntener muy claro al generar una clase si queremos que se pueda heredar de ella o no.\n\nSi no desarrollamos nuestras clases teniendo en cuenta que puedan ser extendidas, pueden dar lugar a fallos y **efectos\nsecundarios**, es por ello que se recomienda extender solo de clases que hayan sido diseñadas para ello. \n\n--- \n\n## 5. La encapsulación\n\n**¿Qué es la encapsulación?**\n\n* Agrupar datos con métodos que operan sobre ellos\n  * En Kotlin esto lo conseguimos con las clases. Las clases siempre son\n    datos y funciones para operar con esos datos\n\n* Blindar datos y códigos del exterior\n\n**¿Por qué usamos encapsulación?**\n\nPo un lado nos interna esconder los detalles de la representación interna de\nnuestros objetos\n\nLa idea es que hacia afuera la clase sea lo más abstracta/enigmática posible\n\nPor ejemplo podemos tener almacenada una lista de amigos, pero cuando otras clases\nllaman a nuestros métodos no saben si la lista está implementada con un array o con\nuna estructura de árbol.\n\nHay más razones, como que solo nosotros conocemos las restricciones que hacen que\nnuestra clase funcione correctamente.\n\n````kotlin\nclass Rectange(var width: Double, var height: Double)\n````\n\n```kotlin\n\nimport java.awt.Rectangle\n\nfun main() {\n  val myRectangle = Rectangle(2.5, 2.6)\n  myRectangle.height = -2.4 // mal\n}\n```\n\n### Blindaje\n\n\u003cimg src=\"./docs/images/blindaje.png\" title=\"blindaje\" width=\"400px\"/\u003e\n\n### Caja negra\n\n\u003cimg src=\"./docs/images/caja-negra.png\" title=\"caja negra\" width=\"400px\"/\u003e\n\n---\n\n## 6. El concepto de null\n\nPuntero o referencia que no apunta a un objeto válido.\n\n### Memoria\n\nVeamos como se organiza la memoria de nuestros programas.\n\nCuando declaramos una variable de referencia lo que en realidad estamos alojando en nuestra\nvariable es la referencia en memoria donde está alojado nuestro objeto.\n\n`````kotlin\nvar nombre = \"Ana\"\n`````\n\n\u003cimg src=\"./docs/images/memoria.png\" width=\"600px\"/\u003e\n\n````kotlin\nvar nombre: String = null\n````\n\n\u003cimg src=\"./docs/images/memoria-null.png\" width=\"600px\"/\u003e\n\nEn concepto de ``null`` es muy útil cuando queremos representar un objeto de clase desconocido\n\n#### Problema\n\nEn Java cuando declaramos una variable tenemos que definir el tipo de objeto que esta contendrá.\n\n````java\nString nombre = \"Ana\"; // También puede contener null\nnombre = null;\nnombre.length(); // ==\u003e NullPointerException ==\u003e Se cierra nuestra aplicación android\n\n// Deberíamos hacer:\n\nif(nombre != null){\n    nombre.length();\n}\n````\n\n#### Solución Kotlin\n\nEn Kotlin y en otros lenguajes modernos se ha optado por trasladar el error para que en lugar de\nque ocurra en tiempo de ejecución se produzca en tiempo de compilación\n\n* `Tiempo de ejecución -\u003e Tiempo de compilación`\n\n* Tipos que aceptan el valor null: debemos declarar si una clase puede o no contener el valor\n  null. Y si puede, nos obliga a comprobarlo antes de operar con ella.\n\n````kotlin\nvar a: String = \"abc\"   // La variable a puede contener un String y no puede contener null\nvar b: String? = \"abc\"  // La variable b puede contener un String o puede contener null\n\nb?.lenght               // Para acceder a las propiedades de b usaremos una llamada segura\nb?.contains('a')        // Para llamar a las funciones miembro de b usaremos una llamada segura\n````\n### El operador Elvis\n\n````kotlin\nvar a: String = \"abc\"\nvar b: String? = \"abc\"\n\n// Asignamos a a el valor de b a no ser que esta sea null, en tal caso le asignamos un String vacío\na = b ?: \"\" \n````\n\n### El operador !!\n\n\n````kotlin\n// Intentaremos no usarlo nunca\nval longitud = b!!.length // Si b es null lanzará NullPointerException\n````\n\n````kotlin\nval word: String? = \"Hello World\"\n\n// Le estamos asegurando de que word nunca será null, en caso de serlo =\u003e NullPointerException\nval anotherWord: String = word!! \n````\n\n--- \n\n## 7. Funciones de ámbito\n\nKotlin nos ofrece en su librería estándar una serie de funciones que pueden mejorar\nla legibilidad de nuestro código que lo hace más conciso y mejora nuestra eficiencia,\ny les llama **funciones de ámbito**.\n\nTodas estas funciones tienen como parámetro una función lambda que crea un ámbito en el\ncontexto de un objeto en el que podemos escribir nuestro código como si estuviéramos en el\námbito de una función del mismo objeto.\n\nExisten cinco funciones de ámbito en la librería estándar de Kotlin\n\n* `let`\n* `run`\n* `with`\n* `apply`\n* `also`\n\nAlgunas son muy similares entre sí y en ocasiones puede resultar difícil decantarse por una de ellas.\n\n### Objeto del Contexto\nUna de las características que las diferencia unas de otras es la referencia que usamos para\nreferirnos al objeto del contexto que crea la expresión lambda.\n\nTenemos dos opciones:\n\n* Funciones que se refieren al objeto con `this`\n  * Las funciones de ámbito que utilizan este tipo de referencia son:\n    * `run`\n    * `with`\n    * `apply`\n  * Una de sus ventajas es que esta referencia se puede omitir. Dando lugar a un código más\n    conciso (ojo: también puede dar lugar a código menos legible).\n  * Se recomienda usar estas funciones cuando en el cuerpo de la lambda solo haya llamadas\n    a las funciones del objeto o cambios en el valor de sus propiedades.\n\n* Funciones que se refieren al objeto con `it`\n  * Las funciones de ámbito que utilizan este tipo de referencia son:\n    * `let`\n    * `also`\n  * Una de sus ventajas es que esta referencia `it` puede sustituirse por otro identificador, mejorando\n    así su legibilidad.\n  * Se recomienda usar estas funciones cuando el objeto es usado como argumento para llamar a otras funciones,\n    y también cuando hay múltiples variables en el cuerpo de la lambda.\n\n\u003cimg src=\"./docs/images/objeto-contexto.png\" width=\"600px\"/\u003e\n\n#### Valor devuelto\n\nOtra cosa que diferencia unas funciones de ámbito de otras es el valor devuelto por las mismas.\n\nHay un grupo de funciones que \u003cu\u003edevuelven el resultado de la lambda\u003c/u\u003e (`let`, `with` y `run` ), que podemos usar cuando\nqueremos obtener un objeto de una clase diferente que resulte de las operaciones con el objeto del contexto. \n\nY hay otras que devuelven el objeto (`apply` y `also`) en sí, y son también muy útiles para encadenar diferentes llamadas a funciones\ndel objeto y para añadir efectos colaterales.\n\n\u003cimg src=\"./docs/images/valor-devuelto.png\" width=\"600px\"/\u003e\n\n### Let\n\nLa función `let` aplicada a un objeto nos devuelve el resultado de la lambda\n\nThe context object is available as an argument (`it`). The return value is the lambda result.\n\n```kotlin\nval str: String? = \"Hello\"\n\nval length = str?.let {\n    println(it) // Solo se ejecuta si str no es null\n}\n\nval myNumber = 5\nval myNumber2 = myNumber.let { it * 2 }\n// Dentro de let podemos referinos a myNumber con it\n// Devuelve el resultado del bloque\n\nval names = listOf\u003cString\u003e(\"Ana\", \"Juan\", \"Luis\", \"Sara\")\nnames.filter { it.contains('a') }\n  .sorted()\n  .let { \n      // aquí tendremos el resultado de encadenar las funciones mediante it\n      println( it ) // [Ana, Juan, Sara]\n  }\n```\n\nLa función `let` suele usarse a menudo para ejecutar código solo en caso de que un valor sea `null`\n\nPor ejemplo:\n\n```kotlin\nfun saludar(names: List\u003cString?\u003e) {\n    for (name in names) {\n//        if (nombre != null ) {\n//            println(\"Hola, $name\")\n//        }\n\n      name?.let { name -\u003e println(\"Hola, $name\") }\n      // renombramos $it para que sea más legible el código\n    }\n}\n```\n\n107.- En el primer escenario, con una única variable y también haciendo\nuso de let, muestra por pantalla el mensaje “¡Es null!” en el caso de que la\nvariable lo sea.\n\n```kotlin\nval nombre: String? = null\nnombre?.let { } ?: run { println(\"¡Es Null!\") }\n```\n\n### With\n\nEs una función de ámbito que tiene dos parámetros:\n\n* Un objeto, que será el receptor (`usuario`)\n* Una expresión lambda (`{ ... }`)\n\nEl resultado de la función `with` es el resultado de la función lambda.\n\n```kotlin\nval numbers = mutableListOf(\"uno\", \"dos\", \"tres\")\n\nwith(numbers) {\n  // Dentro de la lambda ahora nos referimos al objeto mediante $this\n    println(\"'with' es llamado con el argumento $this\")\n    println(\"Contiene $size elementos\")\n}\n```\n\n```kotlin\nusuario.moverA(\"Admin\")\nusuario.changePassword(\"admin\")\n\n// En su lugar:\n\nwith(user) {\n    moverA(\"Admin\") // son funciones de la clase de usuario!\n    changePassword(\"admin\")\n}\n\n// De esta forma queda más limpio\n// La función with nos permite tener un código más ordenado y no tener que estar \n// repitiendo el nombre del objeto.\n// También permite a alguien que lee nuestro código entender que ese grupo de funciones\n// está relacionada\n```\n\nLa función `with` está recomendada cuando se van a usar las funciones miembro de un objeto\ncuando no se va a utilizar el resultado de la lambda. Por lo tanto, es muy cómodo para crear un \námbito donde trabajaremos con el objeto\n\nOtro ejemplo: \n\n```kotlin\nvar usuario = Usuario(\"Alex\", 12345, mail@mail.com)\n\nusuario.nombre = \"Alex123\"\nusuario.id = \"123123\"\n\n// Ahora:\n\nwith(usuario) {\n  nombre = \"Alex123\" \n  id = 12122\n  email = \"otro@mail.com\"\n} \n// más legible\n// normalmente no se quiere obtener un resultado, solo operar sobre el objeto\n// val result = with (usuario) { ... } \u003c== no se hace normalmente\n```\n\n### Run\n\nLa función de ámbito `run` se aplica a un objeto y tiene como parámetro de entrada una expresión\nlambda. El resultado de la función `run` será el resultado de la lambda. \n\nLa función `run` suele usarse cuando necesita implementarse una configuración. Si, además de\nrealizar una acción, necesitamos obtener un resultado de esa acción\n\n```kotlin\nval servicio = MultiportService(\"https://example.kotlinlang.org\", 80)\nval resultado = servicio.run {\n    port = 8080\n    query(prepareRequest() + \" to port $port\")\n}\n```\n\n```kotlin\nval conexion = URL(\"https://es.wikipedia.org\").openConnection()\nval datos = conexion.run {\n    connectTimeout = 2000\n    getInputStram().bufferedReader().readText()\n}\n```\n\n### Apply\n\n`apply` es una función de ámbito que se aplica a un objeto y a la que se le pasa una función lambda. \nEl resultado de la función `apply` es el objeto en sí. Ahora, en lugar de ser el resultado la última\nlínea de la función lambda, el resultado es el objeto. \n\nUn uso muy común de `apply` es la inicialización o la configuración de objetos.\n\nMás adelante utilizaremos esta función en las vistas de android (fragments).\n\n```kotlin\nval adam = Persona(\"Adam\").apply {\n    edad = 32\n    ciudad = \"Madrid\"\n}\n\nprintln(adam)\n```\n\n### Also\n\nLa función de ámbito `also` se aplica a un objeto y lo que devuelve es el mismo objeto\ndespués de ejecutar la función lambda. \n\nLa función `also` funciona muy bien cuando queremos encadenar varias acciones que tienen que \nver unas con otras, especialmente cuando tienen efectos secundarios relacionados. \n\nEsta función se aplica muchas veces en conjunto con `apply`\n\n```kotlin\nval numeros = mutableListOf(\"uno\", \"dos\", \"tres\")\n\nnumeros\n    .also { println(\"La lista de elementos hasta ahora: $it\") }\n    .add(\"cuatro\")\n```\n\nOtro ejemplo: \n\n```kotlin\nval usuarios = mutableListOf\u003cPerson\u003e()\nval newUser = Person(\"Ana Belén\", \"López Iturriaga\", Address(\"Calle Lomo Verdejo\", 2, 350018), \"788455777\")\n  .also { usuarios.add(it) }\n  .also { println(\"Se ha añadido el usuario ${it.firstname}\") }\n```\n\nLo que distingue `also` y `apply` de las demás funciones es que estas lo que nos devuelven es el objeto. \n\n### Take-if y take-unless\n\n```kotlin\nval numero = Random.nextInt(100)\nval parONull = numero.takeIf { it % 2 == 0 }\nval imparONull = numero.takeUnless { it % 2 == 0 }\n```\n\n## Práctica Kotlin\n\n29/9/2022\n\n## Corrutinas\n\n3/10/2022\n\n\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgitfrandu4%2Flearning-kotlin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgitfrandu4%2Flearning-kotlin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgitfrandu4%2Flearning-kotlin/lists"}