Contenidos
- Herencia
- Polimorfismo
- Excepciones
- Anotaciones
- Variables y tipos de datos
- Operadores
- Conversión de entero a cadena (Int a String)
- Conversión de cadena a entero (String a Int)
- Conversión de cadena a flotante (String a Float)
- Conversión de flotante a entero (Float a Int)
- Conversión de entero a flotante (Int a Float)
- Conversión de cadena a entero (String a Int) con parseInt
- Manejo de excepción al convertir una cadena no válida
- Conversión de cadena con signo (String a Int) con parseInt
- Estructuras de Control (if)
- Funciones
- Bucle for simple
- Bucle while
- Bucle do-while
- Bucle for con lista
- Bucle for con índices
- Clases y Objetos
- Colecciones
- Null Safety
- Extension functions
- Tratamiento de excepciones
- Entrada y salida
- Iteración y bucles
- Entrada de datos del usuario
- Funciones recursivas
- Lectura y escritura de archivos
- API de Internet (HTTP)
- Uso de librerías externas (GSON)
- Programación orientada a objetos (Herencia)
- Programación funcional (Funciones de orden superior)
- Programación concurrente (Hilos)
- Expresiones lambda y funciones anónimas
- Programación orientada a eventos (Escuchadores)
- Uso de anotaciones
- Uso de extension functions y properties
- Tratamiento de errores y excepciones personalizadas
- Manejo de listas con funciones de orden superior
- Uso de inicializadores de clase
- Uso de companion objects
- Uso de lambdas con recorrido de mapas
- Uso de interfaces y herencia múltiple
- Uso de enumeraciones (Enums)
- Uso de funciones de extensión para colecciones
- Creación de un objeto con propiedades
- Creación de una clase con un constructor
- Función para verificar un inicio de sesión
- Uso de hash tables
- Uso de listas para gestionar objetos
- Uso de funciones de extensión para listas
- Uso de interfaz
- Uso de enumeraciones (juego de piedra, papel o tijera)
- Uso de JSON
- Uso de listas para gestionar objetos en JSON
- Uso de herencia en Kotlin (ejemplo sobre figuras geométricas)
- Uso de funciones de orden superior (higher-order functions)
- Uso de claves (hash tables) para almacenar objetos JSON
- Uso de funciones closures
- Uso de herencia y polimorfismo
- Uso de funciones de extensión en clases
- Uso de colecciones y mapas
- Uso de funciones de extensión y funciones de orden superior
- Uso de clases selladas (sealed classes)
- Uso de clases abstractas e interfaces
- Uso de propiedades computadas
- Uso de operadores sobrecargados
- Uso de generics
- Uso de clases anidadas
- Uso de objetos singleton
- Uso de operadores infix
- Uso de expresiones lambda con receptor
- Uso de rangos (ranges)
- Uso de colecciones mutable y funciones de modificación
- Uso de mapas (diccionarios) y acceso a elementos
- Uso de expresiones when
- Uso de corrutinas (coroutines)
- Uso de expresiones lambda y high-order functions
- Uso de anotaciones (annotations)
- Uso de expresiones lambdas con receptores de contexto
- Uso de programación orientada a aspectos
- Uso de clases selladas (sealed classes)
- Uso de inicializadores de instancia
- Uso de enumeraciones con métodos
- Uso de extension properties
- Uso de expresiones lambda y funciones de orden superior con colecciones
- Uso de expresiones regulares (Regex)
- Uso de conversión de tipos y seguridad de tipos
- Uso de extension functions con receptores de contexto
- Uso de métodos con argumentos con nombre
- Uso de propiedades delegadas
- Uso de lambdas con with y apply
- Uso de la librería estándar
- Uso de la expresión when con múltiples condiciones
- Uso de cláusulas try y catch con recursos (try with resources)
- Uso de expresiones de rango (range expressions)
- Uso de object expressions
- Uso de expresiones sealed y beneficios en jerarquías de clases
- Uso de anotaciones y reflección
- Uso de corutinas (coroutines) para tareas asíncronas
- Delegados personalizados
- Proyecciones de tipos genéricos (Type projections)
- Uso de expresiones regulares (Regex)
- Infix functions
- Uso de destructuring declarations
- Concurrent collections
- Programación funcional
- Uso de data classes y copy
- Uso de rangos (Ranges)
- Patrón delegado por propiedad (Property Delegation)
- Uso de extension functions (Funciones de extensión) en librerías externas
- Copiar un archivo en otro: Este código copia el contenido de un archivo de origen en un archivo de destino
- Lectura de un archivo de texto: Este código lee y muestra el contenido de un archivo de texto
- Escritura en un archivo de texto: Este código escribe un contenido en un nuevo archivo de texto
- Copiar un archivo de forma simple: Este código copia el contenido de un archivo de origen en un archivo de destino utilizando una función simple
- Eliminar un archivo: Este código elimina un archivo si existe
- Listar archivos en un directorio: Este código lista los archivos en un directorio dado
- Crear un directorio: Este código crea un nuevo directorio en la ruta especificada
- Generación de hash (SHA-256)
- Cifrado simétrico (AES)
- Cifrado César
- Firma digital (RSA)
- Cifrado asimétrico (RSA)
- Generación de contraseñas seguras
- Cifrado simétrico con AES y modo de operación CBC
Herencia
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
open class Animal(val nombre: String) { fun emitirSonido() { println("El $nombre emite un sonido.") } } class Perro(nombre: String) : Animal(nombre) { fun ladrar() { println("El $nombre ladra.") } } fun main() { val perro = Perro("Max") perro.emitirSonido() // Hereda el método de la clase Animal perro.ladrar() // Método propio de la clase Perro } |
Explicación: En este ejemplo, tenemos una clase Animal
con un método emitirSonido
. La clase Perro
hereda de Animal
y agrega su propio método ladrar
. En la función main
, creamos un objeto Perro
y llamamos tanto al método heredado emitirSonido
como al método propio ladrar
.
Polimorfismo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
interface Sonido { fun hacerSonido() } class Gato : Sonido { override fun hacerSonido() { println("El gato maúlla.") } } class Pajaro : Sonido { override fun hacerSonido() { println("El pájaro canta.") } } fun main() { val animales = listOf(Gato(), Pajaro()) for (animal in animales) { animal.hacerSonido() // Polimorfismo en acción } } |
Explicación: En este ejemplo, creamos una interfaz Sonido
con un método hacerSonido
. Las clases Gato
y Pajaro
implementan esta interfaz y proporcionan su propia implementación del método. En la función main
, creamos una lista de animales y llamamos al método hacerSonido
para cada uno, lo que demuestra el polimorfismo.
Excepciones
1 2 3 4 5 |
try { val resultado = 10 / 0 } catch (ex: ArithmeticException) { println("Error: ${ex.message}") } |
Explicación: En este ejemplo, intentamos dividir por cero, lo que genera una excepción ArithmeticException
. La excepción se captura en el bloque catch
y se imprime un mensaje de error que incluye la descripción de la excepción.
Anotaciones
1 2 3 4 5 6 7 8 9 10 11 |
class MiClase { @Override fun miMetodo() { // Implementación del método } } fun main() { val instancia = MiClase() instancia.miMetodo() } |
Explicación: En este ejemplo, utilizamos la anotación @Override
para indicar que el método miMetodo
de la clase MiClase
anula un método de la superclase. Esto es útil para indicar de manera explícita la intención de sobrescribir un método en la herencia.
Variables y tipos de datos
1 2 3 4 5 |
fun main() { // Variables y Tipos de Datos: val nombre = "Juan" // Variable inmutable var edad = 25 // Variable mutable } |
Explicación: En este ejemplo, hemos declarado dos variables: nombre y edad. nombre es una variable inmutable (val) que almacena el valor «Juan», mientras que edad es una variable mutable (var) que almacena el valor 25.
Operadores
1 2 3 4 5 |
fun main() { // Operadores: val suma = 5 + 3 val esIgual = (suma == 8) } |
Explicación: En este ejemplo, exploramos operadores en Kotlin. Hemos utilizado el operador + para realizar una suma y el operador == para comparar valores. La variable suma almacena el resultado de la suma 5 + 3, y esIgual almacena true si suma es igual a 8.
Conversión de entero a cadena (Int a String)
1 2 3 4 5 |
val entero = 42 val cadena = entero.toString() println("Entero: $entero") println("Cadena: $cadena") |
Explicación: En este ejemplo, convertimos un valor entero (42) en una cadena utilizando la función toString().
Conversión de cadena a entero (String a Int)
1 2 3 4 5 |
val cadena = "123" val entero = cadena.toInt() println("Cadena: $cadena") println("Entero: $entero") |
Explicación: Aquí convertimos una cadena que contiene un número (por ejemplo, «123») en un valor entero utilizando la función toInt().
Conversión de cadena a flotante (String a Float)
1 2 3 4 5 |
val cadena = "3.14" val flotante = cadena.toFloat() println("Cadena: $cadena") println("Flotante: $flotante") |
Explicación: En este caso, convertimos una cadena que representa un número de punto flotante (por ejemplo, «3.14») en un valor flotante utilizando toFloat().
Conversión de flotante a entero (Float a Int)
1 2 3 4 5 |
val flotante = 3.99 val entero = flotante.toInt() println("Flotante: $flotante") println("Entero: $entero") |
Explicación: En este ejemplo, realizamos una conversión de un valor flotante (por ejemplo, 3.99) a un valor entero. Toma en cuenta que la parte decimal se trunca en este caso.
Conversión de entero a flotante (Int a Float)
1 2 3 4 5 |
val entero = 42 val flotante = entero.toFloat() println("Entero: $entero") println("Flotante: $flotante") |
Explicación: Aquí realizamos la conversión de un valor entero a un valor flotante utilizando la función toFloat().
Conversión de cadena a entero (String a Int) con parseInt
1 2 3 4 5 |
val cadena = "42" val entero = Integer.parseInt(cadena) println("Cadena: $cadena") println("Entero: $entero") |
Explicación: En este ejemplo, convertimos una cadena que contiene un número (por ejemplo, «42») en un valor entero utilizando Integer.parseInt. La función parseInt es útil para convertir cadenas en valores enteros en Kotlin y manejar posibles excepciones en caso de cadenas no válidas.
Manejo de excepción al convertir una cadena no válida
1 2 3 4 5 6 7 |
val cadena = "abc" try { val entero = Integer.parseInt(cadena) println("Entero: $entero") } catch (e: NumberFormatException) { println("Error: No se puede convertir la cadena en un entero.") } |
Explicación: En este caso, intentamos convertir una cadena no válida («abc») en un entero utilizando Integer.parseInt. Debido a que la cadena no es un número válido, se lanza una excepción NumberFormatException, que manejamos con un bloque try-catch.
Conversión de cadena con signo (String a Int) con parseInt
1 2 3 4 5 6 7 8 |
val cadenaPositiva = "+123" val cadenaNegativa = "-456" val enteroPositivo = Integer.parseInt(cadenaPositiva) val enteroNegativo = Integer.parseInt(cadenaNegativa) println("Cadena Positiva: $cadenaPositiva -> Entero Positivo: $enteroPositivo") println("Cadena Negativa: $cadenaNegativa -> Entero Negativo: $enteroNegativo") |
Explicación: En este ejemplo, convertimos cadenas que incluyen signo («+123» y «-456») en valores enteros utilizando Integer.parseInt. La función maneja los signos correctamente.
Estructuras de Control (if)
1 2 3 4 5 6 7 8 9 |
fun main() { // Estructuras de Control (if): val numero = 5 if (numero > 0) { println("Número positivo") } else { println("Número negativo") } } |
Explicación: En este ejemplo, utilizamos la estructura de control if para tomar decisiones basadas en condiciones. La variable numero contiene el valor 5. Si número es mayor que 0, se imprime «Número positivo». De lo contrario, se imprime «Número negativo».
Funciones
1 2 3 4 5 6 7 |
fun main() { // Funciones: fun saludar(nombre: String) { println("Hola, $nombre") } saludar("Ana") } |
Explicación: En este ejemplo, hemos definido una función llamada saludar que toma un parámetro nombre de tipo String. Dentro de la función, utilizamos println para saludar a la persona cuyo nombre se pasa como argumento. En la función main, llamamos a saludar(«Ana»), lo que imprime «Hola, Ana».
Bucle for simple
1 2 3 |
for (i in 1..5) { println("Iteración") } |
Explicación: Este bucle for itera a través de los valores del 1 al 5 e imprime un mensaje en cada iteración.
Bucle while
1 2 3 4 5 |
var contador = 0 while (contador < 5) { println("Iteración") contador++ } |
Explicación: En este ejemplo, se utiliza un bucle while para imprimir un mensaje en cada iteración mientras se cumple la condición.
Bucle do-while
1 2 3 4 5 |
var numero = 1 do { println("Número: $numero") numero++ } while (numero <= 5) |
Explicación: El bucle do-while ejecuta el bloque de código al menos una vez y luego verifica la condición. En este caso, se imprime el número del 1 al 5.
Bucle for con lista
1 2 3 4 |
val nombres = listOf("Ana", "Juan", "Luis") for (nombre in nombres) { println("Nombre: $nombre") } |
Explicación: En este ejemplo, se utiliza un bucle for para iterar a través de una lista de nombres y mostrar cada nombre.
Bucle for con índices
1 2 3 4 |
val colores = arrayOf("Rojo", "Verde", "Azul") for (indice in colores.indices) { println("Color: ${colores[indice]}") } |
Explicación: Aquí, se utiliza un bucle for para iterar a través de los índices de un array y mostrar el color en cada índice.
Clases y Objetos
1 2 3 4 5 |
fun main() { // Clases y Objetos: class Persona(val nombre: String, var edad: Int) val persona1 = Persona("Carlos", 30) } |
Explicación: En este ejemplo, hemos definido una clase llamada Persona con dos propiedades: nombre (inmutable) y edad (mutable). Luego, hemos creado un objeto llamado persona1 de tipo Persona con los valores «Carlos» y 30 para las propiedades. Las clases se utilizan para modelar objetos y su comportamiento.
Colecciones
1 2 3 4 5 |
fun main() { // Colecciones: val listaNombres = listOf("Ana", "Juan", "Luis") val diccionario = mapOf("clave1" to 10, "clave2" to 20) } |
Explicación: En este ejemplo, estamos trabajando con colecciones en Kotlin. Hemos creado una lista llamada listaNombres que contiene los nombres «Ana,» «Juan,» y «Luis.» Además, hemos definido un diccionario llamado diccionario que asocia las claves «clave1» y «clave2» con los valores 10 y 20. Kotlin proporciona colecciones para almacenar datos de manera estructurada.
Null Safety
1 2 3 4 |
fun main() { // Null Safety: var nombre: String? = null // Puede ser nulo } |
Explicación: En este ejemplo, abordamos el concepto de seguridad en nulos en Kotlin. Hemos declarado una variable llamada nombre de tipo String?, que indica que puede contener un valor nulo. En este caso, hemos asignado null como su valor inicial. Kotlin enfatiza la seguridad en nulos para evitar errores de referencia nula.
Extension functions
1 2 3 4 5 6 7 8 |
fun main() { // Extension Functions: fun String.reverso(): String { return this.reversed() } val palabra = "Hola" val reverso = palabra.reverso() } |
Explicación: En este ejemplo, presentamos las funciones de extensión en Kotlin. Hemos creado una función de extensión llamada reverso para la clase String. Esta función invierte el contenido de la cadena. Luego, en la función main, utilizamos esta función de extensión para invertir la cadena «Hola», y el resultado se almacena en la variable reverso.
Tratamiento de excepciones
1 2 3 4 5 6 7 8 |
fun main() { // Tratamiento de Excepciones: try { val resultado = 10 / 0 } catch (ex: ArithmeticException) { println("Error: ${ex.message}") } } |
Explicación: En este ejemplo, hemos abordado el manejo de excepciones en Kotlin. Utilizamos un bloque try para ejecutar código que podría generar una excepción. En este caso, intentamos dividir 10 por 0, lo cual genera una excepción aritmética. El bloque catch captura la excepción y permite manejarla. Imprimimos un mensaje de error que incluye la descripción de la excepción.
Entrada y salida
1 2 3 4 5 6 |
fun main() { // Entrada y Salida: print("Ingresa tu nombre: ") val nombreInput = readLine() println("Hola, $nombreInput") } |
Explicación: En este ejemplo, mostramos cómo interactuar con la entrada y salida de datos en Kotlin. Utilizamos print para solicitar al usuario que ingrese su nombre y readLine para obtener esa entrada. Luego, utilizamos println para saludar al usuario utilizando su nombre.
Iteración y bucles
1 2 3 4 5 6 |
fun main() { // Iteración y Bucles: for (i in 1..5) { println("Iteración $i") } } |
Explicación: En este ejemplo, demostramos cómo usar un bucle for en Kotlin para realizar una iteración. El bucle imprime «Iteración» seguido del valor de i en cada iteración, desde 1 hasta 5.
Entrada de datos del usuario
1 2 3 4 5 6 7 |
fun main() { // Entrada de Datos del Usuario: print("Ingresa tu edad: ") val edadInput = readLine() val edad = edadInput?.toInt() ?: 0 println("Tienes $edad años.") } |
Explicación: En este ejemplo, solicitamos al usuario que ingrese su edad utilizando print. Luego, leemos la entrada del usuario con readLine. Dado que readLine devuelve una cadena, la convertimos a un entero utilizando toInt(). Si la entrada es nula, asumimos una edad de 0. Finalmente, mostramos la edad ingresada.
Funciones recursivas
1 2 3 4 5 6 7 8 |
fun main() { // Funciones Recursivas: fun factorial(n: Int): Int { return if (n <= 1) 1 else n * factorial(n - 1) } val resultado = factorial(5) println("El factorial de 5 es $resultado") } |
Explicación: En este ejemplo, definimos una función recursiva llamada factorial que calcula el factorial de un número. La función se llama a sí misma de forma recursiva hasta que n llega a 1. El resultado se almacena en la variable resultado y se imprime.
Lectura y escritura de archivos
1 2 3 4 5 6 7 8 9 |
import java.io.File fun main() { // Lectura y Escritura de Archivos: val archivo = File("miarchivo.txt") archivo.writeText("Hola, este es un ejemplo de escritura en archivo.") val contenido = archivo.readText() println("Contenido del archivo: $contenido") } |
Explicación: En este ejemplo, importamos la clase File para trabajar con archivos. Creamos un archivo llamado «miarchivo.txt» y escribimos un mensaje en él. Luego, leemos el contenido del archivo y lo imprimimos.
API de Internet (HTTP)
1 2 3 4 5 6 7 8 9 |
import java.net.URL fun main() { // API de Internet (HTTP): val url = URL("https://jsonplaceholder.typicode.com/posts/1") val conexion = url.openConnection() val respuesta = conexion.getInputStream().bufferedReader().use { it.readText() } println("Respuesta de la API: $respuesta") } |
Explicación: En este ejemplo, utilizamos la clase URL para acceder a una API en línea. Abrimos una conexión a la URL, obtenemos la respuesta y la leemos. El resultado es la respuesta de la API que luego imprimimos.
Uso de librerías externas (GSON)
1 2 3 4 5 6 7 8 9 10 11 |
import com.google.gson.Gson data class Persona(val nombre: String, val edad: Int) fun main() { // Uso de Librerías Externas (GSON): val json = """{"nombre": "Luis", "edad": 28}""" val gson = Gson() val persona = gson.fromJson(json, Persona::class.java) println("Nombre: ${persona.nombre}, Edad: ${persona.edad}") } |
Explicación: En este ejemplo, utilizamos la librería externa GSON para parsear un JSON. Definimos una clase Persona, creamos un JSON, y luego utilizamos GSON para convertirlo en un objeto Persona, del cual imprimimos sus propiedades.
Programación orientada a objetos (Herencia)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
open class Animal(val nombre: String) { fun saludar() { println("Hola, soy un $nombre") } } class Perro(nombre: String) : Animal(nombre) { fun ladrar() { println("Guau, guau") } } fun main() { // Programación Orientada a Objetos (Herencia): val miPerro = Perro("Buddy") miPerro.saludar() miPerro.ladrar() } |
Explicación: En este ejemplo, hemos creado una jerarquía de clases. La clase Animal tiene un método saludar, y la clase Perro hereda de Animal y agrega su propio método ladrar. En la función main, creamos un objeto Perro y llamamos a los métodos.
Programación funcional (Funciones de orden superior)
1 2 3 4 5 6 7 8 |
fun main() { // Programación Funcional (Funciones de Orden Superior): val numeros = listOf(1, 2, 3, 4, 5) val suma = numeros.reduce { acc, numero -> acc + numero } val cuadrados = numeros.map { it * it } println("Suma de números: $suma") println("Cuadrados de números: $cuadrados") } |
Explicación: En este ejemplo, utilizamos funciones de orden superior como reduce y map. Reduce se usa para calcular la suma de una lista de números, y map se usa para obtener una lista de los cuadrados de esos números.
Programación concurrente (Hilos)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import kotlin.concurrent.thread fun main() { // Programación Concurrente (Hilos): val hilo1 = thread { for (i in 1..5) { println("Hilo 1 - Iteración $i") } } val hilo2 = thread { for (i in 1..5) { println("Hilo 2 - Iteración $i") } } hilo1.join() hilo2.join() } |
Explicación: En este ejemplo, hemos utilizado hilos para lograr programación concurrente. Creamos dos hilos que ejecutan iteraciones y los hacemos esperar con join para que se completen antes de continuar.
Expresiones lambda y funciones anónimas
1 2 3 4 5 6 7 8 9 10 11 12 13 |
fun main() { // Expresiones Lambda y Funciones Anónimas: val sumaLambda: (Int, Int) -> Int = { a, b -> a + b } val resultadoLambda = sumaLambda(3, 4) val sumaAnonima = fun(x: Int, y: Int): Int { return x + y } val resultadoAnonimo = sumaAnonima(5, 6) println("Resultado Lambda: $resultadoLambda") println("Resultado Anónimo: $resultadoAnonimo") } |
Explicación: En este ejemplo, hemos definido una expresión lambda que representa una función que toma dos enteros y devuelve su suma. También hemos creado una función anónima con la misma funcionalidad. Luego, llamamos a ambas y mostramos los resultados.
Programación orientada a eventos (Escuchadores)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
interface OnClickListener { fun onClick() } class Boton { var listener: OnClickListener? = null fun clic() { listener?.onClick() } } fun main() { // Programación Orientada a Eventos (Escuchadores): val boton = Boton() boton.listener = object : OnClickListener { override fun onClick() { println("Botón clicado") } } boton.clic() } |
Explicación: En este ejemplo, hemos creado una interfaz OnClickListener y una clase Boton que tiene un escuchador. Luego, hemos definido una implementación anónima de OnClickListener y asignado esta implementación al botón. Al hacer clic en el botón, se activa el escuchador y se imprime un mensaje.
Uso de anotaciones
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@Deprecated("Esta función está obsoleta", ReplaceWith("nuevaFuncion()")) fun funcionObsoleta() { println("Esta función está obsoleta.") } fun nuevaFuncion() { println("Esta es la nueva función.") } fun main() { // Uso de Anotaciones: funcionObsoleta() nuevaFuncion() } |
Explicación: En este ejemplo, hemos utilizado la anotación @Deprecated para marcar una función como obsoleta y proporcionar una sugerencia de reemplazo. Cuando llamamos a la función obsoleta, se muestra un mensaje de advertencia. Luego, llamamos a la nueva función recomendada.
Uso de extension functions y properties
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Rectangulo(var ancho: Int, var alto: Int) fun Rectangulo.area(): Int { return ancho * alto } val Rectangulo.perimetro: Int get() = 2 * (ancho + alto) fun main() { // Uso de Extension Functions y Properties: val rectangulo = Rectangulo(5, 3) val area = rectangulo.area() val perimetro = rectangulo.perimetro println("Área del rectángulo: $area") println("Perímetro del rectángulo: $perimetro") } |
Explicación: En este ejemplo, hemos agregado extension functions y properties a la clase Rectangulo. La función de extensión area calcula el área del rectángulo, y la property de extensión perímetro calcula el perímetro, el get()
en la propiedad perimetro
permite calcular dinámicamente el valor del perímetro cada vez que se accede a esta propiedad sin necesidad de almacenar el resultado previamente. Esto facilita la extensión de clases existentes y la adición de nuevas funcionalidades de manera más flexible. Luego, llamamos a estas extensiones para obtener los resultados.
Tratamiento de errores y excepciones personalizadas
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class DivisionPorCeroException(message: String) : Exception(message) fun dividir(a: Int, b: Int): Int { if (b == 0) { throw DivisionPorCeroException("División por cero no permitida") } return a / b } fun main() { // Tratamiento de Errores y Excepciones Personalizadas: try { val resultado = dividir(10, 0) println("Resultado de la división: $resultado") } catch (ex: DivisionPorCeroException) { println("Error: ${ex.message}") } } |
Explicación: En este ejemplo, hemos definido una excepción personalizada DivisionPorCeroException y una función dividir que arroja esta excepción si se intenta dividir por cero. En la función main, intentamos dividir y manejamos la excepción personalizada.
Manejo de listas con funciones de orden superior
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
data class Producto(val nombre: String, val precio: Double) fun main() { // Manejo de Listas con Funciones de Orden Superior: val productos = listOf( Producto("Laptop", 999.99), Producto("Teléfono", 599.99), Producto("Tablet", 299.99) ) val total = productos.sumByDouble { it.precio } val productosCaros = productos.filter { it.precio > 500.0 } println("Total de compras: $total") println("Productos caros: $productosCaros") } |
Explicación: En este ejemplo, hemos definido una lista de productos y utilizamos funciones de orden superior como sumByDouble para calcular el total de compras y filter para encontrar productos caros. Luego, imprimimos los resultados.
Uso de inicializadores de clase
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Persona { var nombre: String var edad: Int init { nombre = "Juan" edad = 25 } } fun main() { // Uso de Inicializadores de Clase: val persona = Persona() println("Nombre: ${persona.nombre}, Edad: ${persona.edad}") } |
Explicación: En este ejemplo, hemos creado una clase Persona con propiedades nombre y edad. Utilizamos un inicializador (init) para asignar valores iniciales a estas propiedades. Luego, creamos un objeto Persona e imprimimos sus propiedades.
Uso de companion objects
1 2 3 4 5 6 7 8 9 10 11 12 |
class MiClase { companion object { fun mensaje() { println("¡Hola desde el objeto compañero!") } } } fun main() { // Uso de Companion Objects: MiClase.mensaje() } |
Explicación: En este ejemplo, hemos creado una clase MiClase con un objeto compañero (companion object). Dentro de este objeto compañero, hemos definido una función mensaje. Luego, hemos llamado a esta función desde la función main.
Uso de lambdas con recorrido de mapas
1 2 3 4 5 6 7 8 |
fun main() { // Uso de Lambdas con Recorrido de Mapas: val numeros = mapOf("uno" to 1, "dos" to 2, "tres" to 3) numeros.forEach { clave, valor -> println("Clave: $clave, Valor: $valor") } } |
Explicación: En este ejemplo, hemos definido un mapa de números. Luego, hemos utilizado la función forEach junto con una lambda para recorrer el mapa y mostrar las claves y valores.
Uso de interfaces y herencia múltiple
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
interface Nadador { fun nadar() } interface Corredor { fun correr() } class Triatleta : Nadador, Corredor { override fun nadar() { println("Triatleta nadando") } override fun correr() { println("Triatleta corriendo") } } fun main() { // Uso de Interfaces y Herencia Múltiple: val triatleta = Triatleta() triatleta.nadar() triatleta.correr() } |
Explicación: En este ejemplo, hemos definido dos interfaces, Nadador y Corredor. Luego, hemos creado una clase Triatleta que implementa ambas interfaces, permitiendo herencia múltiple. La clase Triatleta implementa las funciones nadar y correr, y en la función main, creamos un objeto Triatleta y llamamos a ambas funciones.
Uso de enumeraciones (Enums)
1 2 3 4 5 6 7 8 9 |
enum class DiaSemana { LUNES, MARTES, MIÉRCOLES, JUEVES, VIERNES, SÁBADO, DOMINGO } fun main() { // Uso de Enumeraciones (Enums): val dia = DiaSemana.MIÉRCOLES println("Hoy es $dia") } |
Explicación: En este ejemplo, hemos definido una enumeración DiaSemana que representa los días de la semana. En la función main, hemos asignado el valor MIÉRCOLES a la variable dia y lo hemos impreso.
Uso de funciones de extensión para colecciones
1 2 3 4 5 6 7 8 9 10 11 |
fun List.promedio(): Double { if (isEmpty()) return 0.0 return sum() / size.toDouble() } fun main() { // Uso de Funciones de Extensión para Colecciones: val numeros = listOf(10, 20, 30, 40, 50) val promedio = numeros.promedio() println("Promedio de los números: $promedio") } |
Explicación: En este ejemplo, hemos definido una función de extensión promedio para listas de enteros. Esta función calcula el promedio de los números en la lista. Luego, en la función main, hemos llamado a esta función en una lista de números y mostrado el resultado.
Creación de un objeto con propiedades
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Coche { var marca: String = "" var modelo: String = "" var año: Int = 0 } fun main() { // Creación de un objeto coche con propiedades: val miCoche = Coche() miCoche.marca = "Toyota" miCoche.modelo = "Corolla" miCoche.año = 2022 println("Marca: ${miCoche.marca}, Modelo: ${miCoche.modelo}, Año: ${miCoche.año}") } |
Explicación: Hemos creado una clase Coche con propiedades como marca, modelo, y año. Luego, hemos creado un objeto miCoche de esta clase y asignado valores a las propiedades para representar un coche.
Creación de una clase con un constructor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Persona(nombre: String, edad: Int) { val nombre: String val edad: Int init { this.nombre = nombre this.edad = edad } } fun main() { // Creación de una clase con un constructor: val persona = Persona("Juan", 30) println("Nombre: ${persona.nombre}, Edad: ${persona.edad}") } |
Explicación: En este ejemplo, hemos creado una clase Persona con un constructor que toma nombre y edad como parámetros. Luego, inicializamos las propiedades nombre y edad en el bloque init. Finalmente, creamos un objeto persona con valores proporcionados y mostramos sus propiedades.
Función para verificar un inicio de sesión
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
data class Usuario(val nombre: String, val contraseña: String) fun verificarInicioSesion(usuario: Usuario): Boolean { val usuariosRegistrados = mapOf( "usuario1" to "contraseña1", "usuario2" to "contraseña2" ) return usuariosRegistrados[usuario.nombre] == usuario.contraseña } fun main() { // Función para verificar un inicio de sesión avanzado: val usuario = Usuario("usuario1", "contraseña1") val inicioSesionExitoso = verificarInicioSesion(usuario) if (inicioSesionExitoso) { println("Inicio de sesión exitoso para ${usuario.nombre}") } else { println("Error en el inicio de sesión") } } |
Explicación: Hemos definido una función verificarInicioSesion que toma un objeto Usuario y verifica si las credenciales coinciden con los usuarios registrados en un mapa. Luego, en la función main, hemos creado un objeto usuario y llamado a la función para verificar el inicio de sesión.
Uso de hash tables
1 2 3 4 5 6 7 8 9 10 |
fun main() { // Uso de Hash Tables: val diccionario = mutableMapOf("clave1" to "valor1", "clave2" to "valor2") diccionario["clave3"] = "valor3" for ((clave, valor) in diccionario) { println("Clave: $clave, Valor: $valor") } } |
Explicación: En este ejemplo, hemos creado una hash table (mutableMapOf) y hemos añadido elementos utilizando la notación de índice. Luego, hemos recorrido la tabla para mostrar las claves y valores.
Uso de listas para gestionar objetos
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
data class Producto(val nombre: String, val precio: Double) fun main() { // Uso de Listas para Gestionar Objetos: val listaProductos = listOf( Producto("Laptop", 999.99), Producto("Teléfono", 599.99), Producto("Tablet", 299.99) ) for (producto in listaProductos) { println("Producto: ${producto.nombre}, Precio: ${producto.precio}") } } |
Explicación: Hemos definido una data class Producto para representar productos con nombre y precio. Luego, hemos creado una lista de productos y la hemos recorrido para mostrar los nombres y precios.
Uso de funciones de extensión para listas
1 2 3 4 5 6 7 8 9 10 11 |
fun List.promedio(): Double { if (isEmpty()) return 0.0 return sum() / size.toDouble() } fun main() { // Uso de Funciones de Extensión para Listas: val numeros = listOf(10, 20, 30, 40, 50) val promedio = numeros.promedio() println("Promedio de los números: $promedio") } |
Explicación: Hemos definido una función de extensión promedio para listas de enteros. Esta función calcula el promedio de los números en la lista. Luego, en la función main, hemos llamado a esta función en una lista de números y mostrado el resultado.
Uso de interfaz
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
interface Figura { fun calcularArea(): Double } class Circulo(val radio: Double) : Figura { override fun calcularArea(): Double { return 3.14 * radio * radio } } class Cuadrado(val lado: Double) : Figura { override fun calcularArea(): Double { return lado * lado } } fun main() { // Uso de Interfaz en Kotlin: val circulo = Circulo(5.0) val cuadrado = Cuadrado(4.0) println("Área del círculo: ${circulo.calcularArea()}") println("Área del cuadrado: ${cuadrado.calcularArea()}") } |
Explicación: Hemos definido una interfaz Figura con un método calcularArea. Luego, hemos creado dos clases, Circulo y Cuadrado, que implementan esta interfaz y proporcionan sus propias implementaciones del método. En la función main, hemos creado objetos de ambas clases y calculado sus áreas.
Uso de enumeraciones (juego de piedra, papel o tijera)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
enum class Juego { PIEDRA, PAPEL, TIJERA } fun jugar(jugador1: Juego, jugador2: Juego) { if (jugador1 == jugador2) { println("Empate") } else if ( (jugador1 == Juego.PIEDRA && jugador2 == Juego.TIJERA) || (jugador1 == Juego.PAPEL && jugador2 == Juego.PIEDRA) || (jugador1 == Juego.TIJERA && jugador2 == Juego.PAPEL) ) { println("Jugador 1 gana") } else { println("Jugador 2 gana") } } fun main() { // Uso de Enumeraciones en un Juego de Piedra, Papel o Tijera: val jugador1 = Juego.PAPEL val jugador2 = Juego.PIEDRA jugar(jugador1, jugador2) } |
Explicación: Hemos definido una enumeración Juego para representar las opciones de Piedra, Papel o Tijera. Luego, hemos creado una función jugar que determina el resultado del juego. En la función main, hemos simulado una partida entre dos jugadores.
Uso de JSON
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json @Serializable data class Persona(val nombre: String, val edad: Int) fun main() { // Uso de JSON en Kotlin: val persona = Persona("Ana", 28) val personaJson = Json.encodeToString(persona) println("Persona en formato JSON: $personaJson") } |
Explicación: En este ejemplo, hemos definido una data class Persona y la hemos marcado como serializable con la anotación @Serializable. Luego, hemos creado un objeto de tipo Persona y lo hemos convertido a formato JSON utilizando la biblioteca de serialización de Kotlin. El resultado se muestra en la consola.
Uso de listas para gestionar objetos en JSON
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json @Serializable data class Producto(val nombre: String, val precio: Double) fun main() { // Uso de Listas para Gestionar Objetos en JSON: val listaProductos = listOf( Producto("Laptop", 999.99), Producto("Teléfono", 599.99), Producto("Tablet", 299.99) ) val listaJson = Json.encodeToString(listaProductos) println("Lista de Productos en formato JSON:\n$listaJson") } |
Explicación: Hemos definido una data class Producto y la hemos marcado como serializable. Luego, hemos creado una lista de productos y la hemos convertido a formato JSON. El resultado muestra la lista de productos en formato JSON.
Uso de herencia en Kotlin (ejemplo sobre figuras geométricas)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
open class Figura { open fun describir(): String { return "Esto es una figura." } } class Cuadrado : Figura() { override fun describir(): String { return "Esto es un cuadrado." } } fun main() { // Uso de Herencia en Kotlin: val figura1: Figura = Figura() val figura2: Figura = Cuadrado() println(figura1.describir()) println(figura2.describir()) } |
Explicación: Hemos creado una clase base Figura con un método describir, que puede ser sobrescrito. Luego, hemos creado una clase Cuadrado que hereda de Figura y sobrescribe el método. En la función main, hemos creado instancias de ambas clases y llamado al método describir.
Uso de funciones de orden superior (higher-order functions)
1 2 3 4 5 6 7 8 9 10 11 12 |
fun operar(a: Int, b: Int, operacion: (Int, Int) -> Int): Int { return operacion(a, b) } fun main() { // Uso de Funciones de Orden Superior (Higher-Order Functions): val suma = operar(5, 3) { x, y -> x + y } val resta = operar(8, 2) { x, y -> x - y } println("Suma: $suma") println("Resta: $resta") } |
Explicación: En este ejemplo, hemos definido una función operar que toma dos números enteros y una función como argumento. Esta función de orden superior realiza una operación con los números proporcionados y la función dada. En la función main, hemos llamado a operar con funciones de suma y resta como argumentos.
Uso de claves (hash tables) para almacenar objetos JSON
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json @Serializable data class Producto(val nombre: String, val precio: Double) fun main() { // Uso de Claves (Hash Tables) para Almacenar Objetos JSON: val producto1 = Producto("Laptop", 999.99) val producto2 = Producto("Teléfono", 599.99) val productos = mutableMapOf( "producto1" to Json.encodeToString(producto1), "producto2" to Json.encodeToString(producto2) ) for ((clave, valor) in productos) { println("Clave: $clave, Valor en formato JSON: $valor") } } |
Explicación: Hemos definido una data class Producto y la hemos marcado como serializable. Luego, hemos creado dos objetos producto1 y producto2. Hemos utilizado una hash table (mutableMapOf) para almacenar estos objetos en formato JSON con claves. Hemos recorrido la tabla para mostrar las claves y valores en formato JSON.
Uso de funciones closures
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
fun contador(): () -> Int { var contador = 0 return { contador++ } } fun main() { // Uso de Funciones Closures en Kotlin: val incrementar = contador() println(incrementar()) // 1 println(incrementar()) // 2 println(incrementar()) // 3 } |
Explicación: Hemos definido una función contador que devuelve una función lambda. Esta función lambda actúa como un cierre (closure) y mantiene un contador interno. En la función main, hemos creado una instancia de contador y llamado a la función lambda para incrementar el contador.
Uso de herencia y polimorfismo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
open class Figura { open fun calcularArea(): Double { return 0.0 } } class Circulo(val radio: Double) : Figura() { override fun calcularArea(): Double { return 3.14 * radio * radio } } class Cuadrado(val lado: Double) : Figura() { override fun calcularArea(): Double { return lado * lado } } fun main() { // Uso de Herencia y Polimorfismo: val figura1: Figura = Circulo(5.0) val figura2: Figura = Cuadrado(4.0) println("Área del círculo: ${figura1.calcularArea()}") println("Área del cuadrado: ${figura2.calcularArea()}") } |
Explicación: Hemos creado una clase base Figura con un método calcularArea que puede ser sobrescrito. Luego, hemos creado dos clases, Circulo y Cuadrado, que heredan de Figura y proporcionan sus propias implementaciones del método calcularArea. En la función main, hemos creado instancias de ambas clases y llamado al método calcularArea. Esto demuestra el polimorfismo, donde las instancias de subclases se comportan como instancias de la clase base.
Uso de funciones de extensión en clases
1 2 3 4 5 6 7 8 9 10 11 12 |
fun String.reverso(): String { return this.reversed() } fun main() { // Uso de Funciones de Extensión en Clases: val palabra = "Kotlin" val reverso = palabra.reverso() println("Palabra original: $palabra") println("Palabra al revés: $reverso") } |
Explicación: Hemos definido una función de extensión reverso para la clase String, que invierte la cadena. En la función main, hemos creado una cadena palabra y llamado a la función de extensión reverso para obtener la cadena al revés.
Uso de colecciones y mapas
1 2 3 4 5 6 7 8 9 10 |
fun main() { // Uso de Colecciones y Mapas en Kotlin: val listaNombres = listOf("Ana", "Juan", "Luis") val conjuntoNumeros = setOf(1, 2, 3, 4, 5) val diccionario = mapOf("clave1" to 10, "clave2" to 20) println("Lista de Nombres: $listaNombres") println("Conjunto de Números: $conjuntoNumeros") println("Diccionario: $diccionario") } |
Explicación: Hemos creado ejemplos de colecciones en Kotlin, incluyendo una lista de nombres, un conjunto de números y un diccionario (mapa) con claves y valores. Luego, hemos mostrado el contenido de estas colecciones.
Uso de funciones de extensión y funciones de orden superior
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
fun List.filtrar(operador: (Int) -> Boolean): List { val resultado = mutableListOf() for (elemento in this) { if (operador(elemento)) { resultado.add(elemento) } } return resultado } fun main() { // Uso de funciones de extensión y funciones de orden superior: val numeros = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) val pares = numeros.filtrar { it % 2 == 0 } val mayoresQueCinco = numeros.filtrar { it > 5 } println("Números pares: $pares") println("Números mayores que cinco: $mayoresQueCinco") } |
Explicación: Hemos definido una función de extensión filtrar para listas de enteros. Esta función toma una función de orden superior como argumento para determinar qué elementos se deben incluir en la lista resultante. Luego, en la función main, hemos utilizado esta función de extensión para filtrar números pares y números mayores que cinco.
Uso de clases selladas (sealed classes)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
sealed class Resultado { data class Exito(val mensaje: String) : Resultado() data class Error(val error: Throwable) : Resultado() } fun procesarResultado(resultado: Resultado) { when (resultado) { is Resultado.Exito -> println("Éxito: ${resultado.mensaje}") is Resultado.Error -> println("Error: ${resultado.error.message}") } } fun main() { // Uso de clases selladas (sealed classes): val exito = Resultado.Exito("Operación completada correctamente") val error = Resultado.Error(Exception("Error en la operación")) procesarResultado(exito) procesarResultado(error) } |
Explicación: Hemos definido una clase sellada Resultado que tiene dos subclases: Exito y Error. En la función procesarResultado, utilizamos una expresión when para manejar diferentes tipos de resultados de manera segura. Luego, en la función main, creamos instancias de éxito y error y las procesamos.
Uso de clases abstractas e interfaces
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
abstract class Figura { abstract fun area(): Double } interface Dibujable { fun dibujar() } class Circulo(val radio: Double) : Figura(), Dibujable { override fun area(): Double { return Math.PI * radio * radio } override fun dibujar() { println("Dibujando un círculo de radio $radio") } } fun main() { // Uso de Clases Abstractas e Interfaces: val circulo = Circulo(5.0) println("Área del círculo: ${circulo.area()}") circulo.dibujar() } |
Explicación: Hemos definido una clase abstracta Figura con un método abstracto area() y una interfaz Dibujable con un método dibujar(). Luego, la clase Circulo implementa ambas, proporcionando una implementación concreta para area() y dibujar().
Uso de propiedades computadas
1 2 3 4 5 6 7 8 9 10 |
class Rectangulo(val ancho: Double, val alto: Double) { val area: Double get() = ancho * alto } fun main() { // Uso de Propiedades Computadas: val rectangulo = Rectangulo(5.0, 3.0) println("Área del rectángulo: ${rectangulo.area}") } |
Explicación: Hemos creado una clase Rectangulo con propiedades ancho y alto. La propiedad area se ha definido como una propiedad calculada, utilizando un getter personalizado para calcular el área a partir de ancho y alto.
Uso de operadores sobrecargados
1 2 3 4 5 6 7 8 9 10 11 12 13 |
data class Punto(val x: Int, val y: Int) operator fun Punto.plus(otro: Punto): Punto { return Punto(x + otro.x, y + otro.y) } fun main() { // Uso de Operadores Sobrecargados: val punto1 = Punto(3, 4) val punto2 = Punto(1, 2) val suma = punto1 + punto2 println("Suma de puntos: ($suma.x, ${suma.y})") } |
Explicación: Hemos creado una clase Punto y sobrecargado el operador + para permitir la suma de dos puntos. Esto nos permite realizar operaciones de suma personalizadas entre objetos de tipo Punto. En esta línea, punto1 + punto2 utiliza el operador +, que se ha sobrecargado mediante la función Punto.plus. La función Punto.plus toma un objeto Punto adicional (otro) y devuelve un nuevo objeto Punto cuyas coordenadas son la suma de las coordenadas de punto1 y punto2. En otras palabras, suma contendrá la suma de las coordenadas x e y de punto1 y punto2.
Uso de generics
1 2 3 4 5 6 7 8 9 10 |
class Caja(val contenido: T) fun main() { // Uso de Generics: val cajaEntero = Caja(42) val cajaCadena = Caja("Hola, Kotlin!") println("Contenido de la caja entero: ${cajaEntero.contenido}") println("Contenido de la caja cadena: ${cajaCadena.contenido}") } |
Explicación: Hemos creado una clase genérica Caja que puede contener cualquier tipo de objeto. Esto nos permite utilizar la misma clase para envolver tanto enteros como cadenas. La T en la declaración class Caja(val contenido: T) se refiere a un tipo genérico. En Kotlin, los tipos genéricos son utilizados para crear clases, funciones o estructuras de datos que pueden trabajar con diferentes tipos de datos de manera flexible. La clase Caja es genérica, lo que significa que puede contener cualquier tipo de dato. La T es simplemente un marcador o nombre de tipo genérico que se utiliza como un comodín para representar un tipo concreto.
En val cajaEntero = Caja(42), la T se infiere automáticamente como Int debido al valor 42 que se pasa como contenido, y en val cajaCadena = Caja(«Hola, Kotlin!»), la T se infiere como String debido al valor «Hola, Kotlin!». Este enfoque genérico permite que la misma clase Caja se utilice con diferentes tipos de datos sin necesidad de crear una clase diferente para cada tipo.
Uso de clases anidadas
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Escuela(val nombre: String) { class Estudiante(val nombre: String) { fun presentarse() { println("Hola, soy $nombre") } } } fun main() { // Uso de Clases Anidadas: val estudiante = Escuela.Estudiante("Ana") estudiante.presentarse() } |
Explicación: Hemos definido una clase Escuela con una clase anidada Estudiante. Las clases anidadas son útiles para organizar y encapsular conceptos relacionados. Hemos creado una instancia de Estudiante y llamado al método presentarse().
Uso de objetos singleton
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
object Registro { private val historial = mutableListOf() fun agregarEvento(evento: String) { historial.add(evento) } fun mostrarHistorial() { for (evento in historial) { println(evento) } } } fun main() { // Uso de Objetos Singleton: Registro.agregarEvento("Inicio de sesión") Registro.agregarEvento("Cierre de sesión") Registro.mostrarHistorial() } |
Explicación: Hemos creado un objeto singleton llamado Registro, que garantiza que solo haya una instancia en todo el programa. Utilizamos este objeto para llevar un historial de eventos, agregar eventos y mostrar el historial.
Uso de operadores infix
1 2 3 4 5 6 7 8 9 10 11 12 13 |
data class Punto(val x: Int, val y: Int) { infix fun hacia(otro: Punto): Punto { return Punto(otro.x - x, otro.y - y) } } fun main() { // Uso de Operadores Infix: val punto1 = Punto(3, 4) val punto2 = Punto(1, 2) val distancia = punto1 hacia punto2 println("Distancia: (${distancia.x}, ${distancia.y})") } |
Explicación: Hemos definido una clase Punto con un operador infix llamado hacia que calcula la distancia entre dos puntos. Luego, en la función main, hemos utilizado este operador de manera más legible.
Uso de expresiones lambda con receptor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
fun persona(cuerpo: Persona.() -> Unit): Persona { val p = Persona() p.cuerpo() return p } class Persona { var nombre: String = "" var edad: Int = 0 } fun main() { // Uso de Expresiones Lambda con Receptor: val persona1 = persona { nombre = "Ana" edad = 30 } println("Nombre: ${persona1.nombre}, Edad: ${persona1.edad}") } |
Explicación: Hemos creado una función persona que acepta una expresión lambda con receptor y la aplica a una instancia de la clase Persona. Esto permite una construcción más fluida de objetos.
Uso de rangos (ranges)
1 2 3 4 5 6 7 8 9 |
fun main() { // Uso de Rangos (Ranges): val rangoNumerico = 1..10 val letras = 'A'..'Z' println("Números en el rango: $rangoNumerico") println("Letras en el rango: $letras") println("¿7 está en el rango? ${7 in rangoNumerico}") } |
Explicación: Hemos creado rangos numéricos y de caracteres. Los rangos son útiles para verificar si un valor se encuentra dentro de un rango específico.
Uso de colecciones mutable y funciones de modificación
1 2 3 4 5 6 7 8 9 10 |
fun main() { // Uso de Colecciones Mutable y Funciones de Modificación: val numeros = mutableListOf(1, 2, 3, 4, 5) numeros.add(6) numeros.removeAt(1) numeros.sort() println("Lista de números modificada: $numeros") } |
Explicación: Hemos creado una lista mutable y utilizado funciones para modificarla. add agrega un elemento, removeAt elimina un elemento por índice y sort ordena la lista.
Uso de mapas (diccionarios) y acceso a elementos
1 2 3 4 5 6 7 |
fun main() { // Uso de Mapas (Diccionarios) y Acceso a Elementos: val diccionario = mapOf("uno" to 1, "dos" to 2, "tres" to 3) val valor = diccionario["dos"] println("Valor asociado a 'dos': $valor") } |
Explicación: Hemos creado un diccionario utilizando mapOf y luego accedido a un elemento específico utilizando la clave.
Uso de expresiones when
1 2 3 4 5 6 7 8 9 10 11 12 13 |
fun clasificar(valor: Any): String { return when (valor) { is Int -> "Es un número entero" is String -> "Es una cadena de texto" else -> "No sé qué es" } } fun main() { // Uso de Expresiones When: val resultado = clasificar(42) println(resultado) } |
Explicación: Hemos definido una función clasificar que utiliza una expresión when para determinar el tipo de valor y devolver una descripción.
Uso de corrutinas (coroutines)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import kotlinx.coroutines.* fun main() { // Uso de Corrutinas: runBlocking { val job = launch { delay(1000) println("¡Hola desde la corrutina!") } println("Esperando a la corrutina...") job.join() println("Corrutina terminada.") } } |
Explicación: Hemos utilizado el módulo kotlinx.coroutines para crear una corrutina. La corrutina se ejecuta de forma asíncrona y podemos esperar su finalización con job.join().
Uso de expresiones lambda y high-order functions
1 2 3 4 5 6 7 8 9 10 |
fun operar(numeros: List, operacion: (Int) -> Int): List { return numeros.map(operacion) } fun main() { // Uso de Expresiones Lambda y High-Order Functions: val numeros = listOf(1, 2, 3, 4, 5) val cuadrados = operar(numeros) { it * it } println("Cuadrados: $cuadrados") } |
Explicación: Hemos definido una función operar que acepta una lista de números y una función de operación. Usamos una expresión lambda para aplicar la operación a cada elemento de la lista.
Uso de anotaciones (annotations)
1 2 3 4 5 6 7 8 9 10 11 12 |
@Target(AnnotationTarget.CLASS) annotation class MiAnotacion(val mensaje: String) @MiAnotacion("Clase anotada") class Ejemplo fun main() { // Uso de Anotaciones: val clase = Ejemplo::class.java val anotacion = clase.getAnnotation(MiAnotacion::class.java) println("Mensaje de la anotación: ${anotacion?.mensaje}") } |
Explicación: Hemos creado una anotación MiAnotacion y la hemos aplicado a una clase llamada Ejemplo. Luego, hemos utilizado la reflexión para obtener y mostrar el mensaje de la anotación.
Uso de expresiones lambdas con receptores de contexto
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
fun persona(lambda: Persona.() -> Unit): Persona { val p = Persona() p.lambda() return p } class Persona { var nombre: String = "" var edad: Int = 0 } fun main() { // Uso de Expresiones Lambdas con Receptores de Contexto: val ana = persona { nombre = "Ana" edad = 30 } println("Nombre: ${ana.nombre}, Edad: ${ana.edad}") } |
Explicación: Hemos definido una función persona que acepta una expresión lambda con un receptor de contexto (Persona) y la aplica a una instancia de la clase Persona. Esto permite una construcción más fluida de objetos.
Uso de programación orientada a aspectos
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
@Target(AnnotationTarget.FUNCTION) annotation class MiAnotacion(val mensaje: String) @MiAnotacion("Antes de ejecutar") fun antesDeEjecutar() { println("Ejecutando antes de la función") } @MiAnotacion("Después de ejecutar") fun despuesDeEjecutar() { println("Ejecutando después de la función") } fun funcionPrincipal() { println("Función principal") } fun main() { // Uso de Programación Orientada a Aspectos: val metodos = ::funcionPrincipal.javaClass.declaredMethods for (metodo in metodos) { val anotacion = metodo.getAnnotation(MiAnotacion::class.java) if (anotacion != null) { println("Anotación '${anotacion.mensaje}' en función: ${metodo.name}") when (anotacion.mensaje) { "Antes de ejecutar" -> antesDeEjecutar() "Después de ejecutar" -> despuesDeEjecutar() } } } funcionPrincipal() } |
Explicación: Hemos utilizado anotaciones (MiAnotacion) para marcar métodos que se ejecutan antes y después de una función principal. Luego, hemos utilizado la reflexión para detectar y ejecutar estos métodos.
Uso de clases selladas (sealed classes)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
sealed class Resultado data class Exito(val mensaje: String) : Resultado() data class Error(val error: String) : Resultado() fun procesarResultado(resultado: Resultado) { when (resultado) { is Exito -> println("Éxito: ${resultado.mensaje}") is Error -> println("Error: ${resultado.error}") } } fun main() { // Uso de Clases Selladas: val exito = Exito("Operación completada") val error = Error("Error inesperado") procesarResultado(exito) procesarResultado(error) } |
Explicación: Hemos definido una clase sellada (Resultado) con dos subclases (Exito y Error). Esta estructura es útil para representar resultados en un estilo seguro y exhaustivo.
Uso de inicializadores de instancia
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Libro { val titulo: String val autor: String init { titulo = "Mi Libro" autor = "Autor Anónimo" } } fun main() { // Uso de Inicializadores de Instancia: val miLibro = Libro() println("Título: ${miLibro.titulo}, Autor: ${miLibro.autor}") } |
Explicación: Hemos creado una clase Libro con propiedades titulo y autor. Dentro del inicializador de instancia (init), hemos asignado valores iniciales a estas propiedades.
Uso de enumeraciones con métodos
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
enum class DiaSemana(val abreviatura: String) { LUNES("Lun"), MARTES("Mar"), MIERCOLES("Mié"), JUEVES("Jue"), VIERNES("Vie"), SABADO("Sáb"), DOMINGO("Dom"); fun esDiaLaboral(): Boolean { return this != SABADO && this != DOMINGO } } fun main() { // Uso de Enumeraciones con Métodos: val dia = DiaSemana.VIERNES println("Día: $dia, Abreviatura: ${dia.abreviatura}") println("¿Es día laboral? ${dia.esDiaLaboral()}") } |
Explicación: Hemos definido una enumeración DiaSemana que representa los días de la semana. También hemos agregado un método esDiaLaboral que indica si un día es laboral o no.
Uso de extension properties
1 2 3 4 5 6 7 8 |
val String.longitud: Int get() = this.length fun main() { // Uso de Extension Properties: val texto = "Hola, mundo" println("Longitud del texto: ${texto.longitud}") } |
Explicación: Hemos creado una propiedad de extensión longitud para la clase String que calcula la longitud del texto. Esto permite agregar propiedades a clases existentes.
Uso de expresiones lambda y funciones de orden superior con colecciones
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
data class Producto(val nombre: String, val precio: Double) fun main() { val listaProductos = listOf( Producto("Laptop", 1200.0), Producto("Teléfono", 600.0), Producto("Tablet", 350.0) ) // Uso de Expresiones Lambda y Funciones de Orden Superior con Colecciones: val total = listaProductos .filter { it.precio > 500 } .map { it.precio * 1.1 } .sum() println("Total de productos caros: $total") } |
Explicación: Hemos creado una lista de productos y utilizado expresiones lambda y funciones de orden superior para filtrar los productos caros (precio > 500), aplicar un impuesto (precio * 1.1) y calcular el total.
Uso de expresiones regulares (Regex)
1 2 3 4 5 6 7 8 9 10 |
fun main() { // Uso de Expresiones Regulares (Regex): val texto = "¡Hola, mundo!123" val patron = "\\d+".toRegex() val numerosEncontrados = patron.findAll(texto) for (numero in numerosEncontrados) { println("Número encontrado: ${numero.value}") } } |
Explicación: Hemos utilizado una expresión regular para encontrar números en un texto. La función findAll devuelve todas las coincidencias y las hemos impreso.
Uso de conversión de tipos y seguridad de tipos
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
fun imprimirLongitud(cadena: Any) { if (cadena is String) { val longitud = cadena.length println("La longitud de la cadena es $longitud") } else { println("No es una cadena") } } fun main() { // Uso de Conversión de Tipos y Seguridad de Tipos: val texto = "Hola, Kotlin" val numero = 42 imprimirLongitud(texto) imprimirLongitud(numero) } |
Explicación: Hemos creado una función imprimirLongitud que demuestra la conversión segura de tipos en Kotlin. Si el argumento es una cadena, calcula su longitud; de lo contrario, muestra un mensaje.
Uso de extension functions con receptores de contexto
1 2 3 4 5 6 7 8 9 10 11 |
data class Usuario(val nombre: String, val edad: Int) fun Usuario.saludar() { println("Hola, mi nombre es $nombre y tengo $edad años.") } fun main() { // Uso de Extension Functions con Receptores de Contexto: val usuario = Usuario("Ana", 28) usuario.saludar() } |
Explicación: Hemos creado una extensión de la clase Usuario que agrega la función saludar(). Esto permite que los objetos de tipo Usuario llamen a esta función como si fuera un método de la clase.
Uso de métodos con argumentos con nombre
1 2 3 4 5 6 7 8 9 |
fun saludar(nombre: String, mensaje: String = "Hola") { println("$mensaje, $nombre") } fun main() { // Uso de Métodos con Argumentos con Nombre: saludar("Ana", "¡Hola!") saludar(nombre = "Carlos") } |
Explicación: Hemos definido una función saludar con argumentos con nombre. Esto permite especificar los argumentos en un orden diferente y proporcionar valores predeterminados.
Uso de propiedades delegadas
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import kotlin.properties.Delegates class Temperatura { var valor: Int by Delegates.observable(20) { _, antiguo, nuevo -> println("La temperatura cambió de $antiguo°C a $nuevo°C") } } fun main() { // Uso de Propiedades Delegadas: val temperatura = Temperatura() temperatura.valor = 25 temperatura.valor = 30 } |
Explicación: Hemos utilizado propiedades delegadas para observar cambios en la temperatura. Cuando se cambia el valor, se dispara un evento que muestra la temperatura anterior y nueva.
Uso de lambdas con with y apply
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
data class Persona(var nombre: String, var edad: Int) fun main() { // Uso de Lambdas con `with` y `apply`: val persona = Persona("Juan", 30) // Utilizando `with` para realizar múltiples operaciones en la misma instancia. with(persona) { nombre = "Ana" edad = 28 } // Utilizando `apply` para realizar operaciones en una instancia y devolverla. val persona2 = Persona("Carlos", 25).apply { nombre = "Luis" edad = 22 } println(persona) // Muestra: Persona(nombre=Ana, edad=28) println(persona2) // Muestra: Persona(nombre=Luis, edad=22) } |
Explicación: Hemos utilizado las funciones de orden superior with y apply para realizar operaciones en una instancia de la clase Persona. with se usa para múltiples operaciones en la misma instancia, mientras que apply permite realizar operaciones y devolver la instancia modificada.
Uso de la librería estándar
1 2 3 4 5 6 7 8 9 10 11 12 |
fun main() { // Uso de la Librería Estándar de Kotlin: val listaNumeros = listOf(1, 2, 3, 4, 5) // Filtrar los números pares y elevar al cuadrado. val resultado = listaNumeros .filter { it % 2 == 0 } .map { it * it } .reduce { acc, valor -> acc + valor } println("Resultado: $resultado") // Muestra: 20 } |
Explicación: Hemos utilizado las funciones de la librería estándar de Kotlin, como filter, map, y reduce, para realizar operaciones en una lista de números. En este caso, hemos filtrado los números pares, elevado al cuadrado y sumado.
Uso de la expresión when con múltiples condiciones
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
fun evaluarDato(dato: Any) { when (dato) { is String -> println("Es una cadena") is Int -> println("Es un entero") is Double -> println("Es un número decimal") else -> println("No se reconoce el tipo") } } fun main() { // Uso de la Expresión `when` con Múltiples Condiciones: evaluarDato("Hola") evaluarDato(42) evaluarDato(3.14) evaluarDato(true) } |
Explicación: Hemos utilizado la expresión when con múltiples condiciones para determinar el tipo de dato y mostrar un mensaje correspondiente. Si el tipo no se reconoce, se muestra un mensaje predeterminado.
Uso de cláusulas try y catch con recursos (try with resources)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import java.io.BufferedReader import java.io.FileReader fun leerArchivo(nombreArchivo: String): String { val archivo = FileReader(nombreArchivo) val buffer = BufferedReader(archivo) return try { buffer.readLine() } catch (ex: Exception) { "Error al leer el archivo: ${ex.message}" } finally { buffer.close() } } fun main() { // Uso de Cláusulas `try` y `catch` con Recursos: val contenido = leerArchivo("miarchivo.txt") println("Contenido del archivo: $contenido") } |
Explicación: Hemos utilizado las cláusulas try y catch para manejar excepciones al leer un archivo. Además, hemos utilizado la cláusula finally para asegurarnos de que el recurso se cierre correctamente.
Uso de expresiones de rango (range expressions)
1 2 3 4 5 6 7 8 9 10 |
fun main() { // Uso de Expresiones de Rango: for (i in 1..5) { println("Número: $i") } val nombres = listOf("Ana", "Juan", "Luis", "María") val sublista = nombres.subList(1, 3) println("Sublista: $sublista") } |
Explicación: Hemos utilizado expresiones de rango para iterar sobre números y crear sublistas. En el ejemplo, hemos iterado del 1 al 5 y creado una sublista de nombres.
Uso de object expressions
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
interface Saludador { fun saludar(): String } fun main() { // Uso de Object Expressions: val saludoPersonalizado = object : Saludador { override fun saludar(): String { return "¡Hola, amigo!" } } println(saludoPersonalizado.saludar()) } |
Explicación: Hemos utilizado object expressions para crear una instancia anónima que implementa la interfaz Saludador. Esto nos permite definir una implementación personalizada de la interfaz sin crear una clase separada.
Uso de expresiones sealed y beneficios en jerarquías de clases
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
sealed class FormaGeometrica { class Circulo(val radio: Double) : FormaGeometrica() class Cuadrado(val lado: Double) : FormaGeometrica() class Triangulo(val base: Double, val altura: Double) : FormaGeometrica() } fun calcularArea(forma: FormaGeometrica): Double { return when (forma) { is FormaGeometrica.Circulo -> Math.PI * forma.radio * forma.radio is FormaGeometrica.Cuadrado -> forma.lado * forma.lado is FormaGeometrica.Triangulo -> 0.5 * forma.base * forma.altura } } fun main() { // Uso de Expresiones `sealed` y Beneficios en Jerarquías de Clases: val circulo = FormaGeometrica.Circulo(5.0) val cuadrado = FormaGeometrica.Cuadrado(4.0) val triangulo = FormaGeometrica.Triangulo(3.0, 6.0) val areaCirculo = calcularArea(circulo) val areaCuadrado = calcularArea(cuadrado) val areaTriangulo = calcularArea(triangulo) println("Área del círculo: $areaCirculo") println("Área del cuadrado: $areaCuadrado") println("Área del triángulo: $areaTriangulo") } |
Explicación: Hemos utilizado la expresión sealed para crear una jerarquía de clases sellada que representa formas geométricas. Esto permite que el compilador verifique que todas las subclases se manejen en la expresión when. Luego, calculamos el área de diferentes formas geométricas.
Uso de anotaciones y reflección
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@Target(AnnotationTarget.CLASS) annotation class MiAnotacion(val mensaje: String) @MiAnotacion("Clase de ejemplo") class MiClase fun main() { // Uso de Anotaciones y Reflección: val clase = MiClase::class val anotacion = clase.annotations.find { it.annotationClass == MiAnotacion::class } if (anotacion != null && anotacion is MiAnotacion) { println("Mensaje de anotación: ${anotacion.mensaje}") } } |
Explicación: Hemos creado una anotación personalizada MiAnotacion y aplicado esta anotación a la clase MiClase. Luego, utilizamos la reflección para obtener la anotación y su mensaje asociado.
Uso de corutinas (coroutines) para tareas asíncronas
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch fun main() { // Uso de Corutinas para Tareas Asíncronas: println("Inicio del programa") GlobalScope.launch { delay(1000) println("Tarea asincrónica completada") } println("Programa en ejecución") Thread.sleep(2000) println("Fin del programa") } |
Explicación: Hemos utilizado corutinas para realizar una tarea asincrónica (simulada con delay) sin bloquear el hilo principal. Esto permite que otras tareas se ejecuten mientras la corutina está en espera.
Delegados personalizados
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty class Ejemplo { var propiedad: String by MiDelegado() fun mostrarPropiedad() { println("Valor de la propiedad: $propiedad") } } class MiDelegado : ReadWriteProperty<Ejemplo, String> { private var valor: String = "Valor Inicial" override fun getValue(thisRef: Ejemplo, property: KProperty<*>): String { return valor } override fun setValue(thisRef: Ejemplo, property: KProperty<*>, value: String) { valor = value } } fun main() { val ejemplo = Ejemplo() ejemplo.mostrarPropiedad() ejemplo.propiedad = "Nuevo Valor" ejemplo.mostrarPropiedad() } |
Explicación: Hemos creado un delegado personalizado MiDelegado que permite controlar el acceso y modificación de una propiedad en una clase. Esto es útil para implementar lógica personalizada al establecer o acceder a valores.
Proyecciones de tipos genéricos (Type projections)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
fun imprimirLista(lista: List<*>) { for (elemento in lista) { println(elemento) } } fun main() { val listaEnteros: List = listOf(1, 2, 3) val listaCadenas: List = listOf("A", "B", "C") println("Lista de Enteros:") imprimirLista(listaEnteros) println("Lista de Cadenas:") imprimirLista(listaCadenas) } |
Explicación: Hemos utilizado proyecciones de tipos genéricos (List<*) para permitir que la función imprimirLista imprima listas de diferentes tipos sin conocer el tipo exacto en tiempo de compilación.
Uso de expresiones regulares (Regex)
1 2 3 4 5 6 7 8 9 10 11 |
fun main() { // Uso de Expresiones Regulares (Regex): val patron = Regex("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}") val coincidencias = patron.findAll(texto) for (matchResult in coincidencias) { val correo = matchResult.value println("Correo encontrado: $correo") } } |
Explicación: Hemos utilizado expresiones regulares (regex) para buscar patrones de correos electrónicos en un texto. Kotlin admite el uso de expresiones regulares de manera efectiva.
Infix functions
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Calculadora(var valor: Int) { infix fun sumar(otroValor: Int): Calculadora { valor += otroValor return this } } fun main() { val calculadora = Calculadora(10) val resultado = calculadora sumar 5 println("Resultado: ${resultado.valor}") } |
Explicación: Hemos definido una función infix llamada sumar que permite realizar una suma en notación infija, lo que hace que la llamada sea más legible y natural.
Uso de destructuring declarations
1 2 3 4 5 6 7 8 9 |
data class Punto(val x: Int, val y: Int) fun main() { val punto = Punto(3, 7) val (coordX, coordY) = punto println("Coordenada X: $coordX") println("Coordenada Y: $coordY") } |
Explicación: Hemos utilizado la funcionalidad de declaraciones de destructuración para extraer los componentes de un objeto de datos (Punto) de manera concisa.
Concurrent collections
1 2 3 4 5 6 7 8 9 10 11 12 |
import java.util.concurrent.CopyOnWriteArrayList fun main() { val listaConcurrente = CopyOnWriteArrayList() listaConcurrente.add(1) listaConcurrente.add(2) for (elemento in listaConcurrente) { listaConcurrente.add(3) println("Elemento: $elemento") } } |
Explicación: Hemos utilizado CopyOnWriteArrayList para crear una colección concurrente que permite la modificación segura mientras se itera sobre ella en un hilo separado.
Programación funcional
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
fun main() { // Programación Funcional en Kotlin: val numeros = listOf(1, 2, 3, 4, 5) // Map para transformar elementos val cuadrados = numeros.map { it * it } // Filter para seleccionar elementos val pares = numeros.filter { it % 2 == 0 } // Reduce para combinar elementos val suma = numeros.reduce { acc, num -> acc + num } println("Cuadrados: $cuadrados") println("Números pares: $pares") println("Suma: $suma") } |
Explicación: Hemos utilizado funciones de la programación funcional como map, filter, y reduce para transformar, seleccionar y combinar elementos de una lista.
Uso de data classes y copy
1 2 3 4 5 6 7 8 9 10 |
data class Persona(val nombre: String, val edad: Int) fun main() { // Uso de Data Classes y Copy: val persona1 = Persona("Juan", 30) val persona2 = persona1.copy(nombre = "Ana") println("Persona 1: $persona1") println("Persona 2: $persona2") } |
Explicación: Hemos creado una data class Persona y utilizado la función copy para crear una copia de un objeto con algunos valores modificados.
Uso de rangos (Ranges)
1 2 3 4 5 6 7 8 9 10 11 |
fun main() { // Uso de Rangos (Ranges): val rango = 1..5 // Rango de 1 a 5 val letras = 'a'..'z' // Rango de letras val estaEnRango = 3 in rango val estaEnLetras = 'x' in letras println("¿3 está en el rango? $estaEnRango") println("¿'x' está en el rango de letras? $estaEnLetras") } |
Explicación: Hemos creado y utilizado rangos para verificar si un valor está dentro de un rango de números o caracteres.
Patrón delegado por propiedad (Property Delegation)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import kotlin.properties.Delegates class Ejemplo { var contador: Int by Delegates.observable(0) { _, old, new -> println("Valor cambiado de $old a $new") } } fun main() { // Patrón Delegado por Propiedad: val ejemplo = Ejemplo() ejemplo.contador = 5 ejemplo.contador = 10 } |
Explicación: Hemos utilizado el patrón de delegado por propiedad para observar cambios en una propiedad y ejecutar una acción cuando cambia su valor.
Uso de extension functions (Funciones de extensión) en librerías externas
1 2 3 |