05. Kotlin – Lambdas, Try..Catch y otros temas avanzados

1. Funciones de Extensión

Las funciones de extensión son una característica poderosa que te permite agregar nuevas funciones a las clases existentes sin modificar su código fuente original. Las funciones de extensión permiten extender las capacidades de una clase sin tener que heredar de ella o modificar directamente su implementación.

fun String.saludo() {
    println("¡Hola, $this!")
}
fun main() {
    val nombre = "Juan"
    nombre.saludo() // Imprime "¡Hola, Juan!"
}
class cli(val nombre:String){
    fun saludo(){
        println(this.nombre)
    }

}

fun cli.saludoPlus(){
    println("Hola ${this.nombre}")
}


fun main(){
    var a=cli("Javi")
    a.saludo()
    a.saludoPlus()
}
fun String.repetirVeces(veces: Int): String {
    return this.repeat(veces)
}

fun main() {
    val texto = "Hola"
    val resultado = texto.repetirVeces(3)
    println(resultado) // Imprime "HolaHolaHola"
}

2. Funciones de orden superior

Las funciones de orden superior son funciones que pueden recibir funciones como parámetros o devolver funciones como resultado.

fun suma(a: Int, b: Int): Int {
    return a + b
}

fun multiplicacion(a: Int, b: Int): Int {
    return a * b
}

fun main() {
    val resultadoSuma = operar(2, 3, ::suma) // Pasando la función suma como argumento
    println(resultadoSuma) // Imprime 5

    val resultadoMultiplicacion = operar(4, 5, ::multiplicacion) // Pasando la función multiplicacion como argumento
    println(resultadoMultiplicacion) // Imprime 20
}

3. Lambdas

Las lambdas son funciones anónimas que se pueden utilizar como argumentos en las funciones de orden superior.

private fun calculadora(n1: Int, n2: Int, fn: (Int, Int) -> Int): Int {
    return fn(n1, n2)
}

fun multiplica(a: Int, b: Int): Int {
    return a * b
}

fun main() {
    println("La operacion 80 * 20 es: ${calculadora(80, 20, ::multiplica)}")

    var funcion = { x: Int, y: Int -> x + y }
    println("la operacion de 80 y 20 es: ${calculadora(80, 20, funcion)}")

    funcion = { x: Int, y: Int -> x - y }
    println("la operacion de 80 y 20 es: ${calculadora(80, 20, funcion)}")

    println("la operacion de 80 y 20 lambda: ${calculadora(80, 20, { x: Int, y: Int -> x + y })}")
    println("la operacion de 80 y 20 lambda: ${calculadora(80, 20, { x: Int, y: Int -> x - y })}")

    println("La potencia de 2 elevado a 5 con lambda es ${
        calculadora(2, 5,
            { x, y ->
                var valor = 1
                for (i in 1..y) valor *= x
                valor
            }
        )
    }")

    println("La potencia de 2 elevado a 5 con lambda es ${
        calculadora(2, 5)
        { x, y ->
            var valor = 1
            for (i in 1..y) valor *= x
            valor
        }
    }")
}

La variable it es una forma abreviada de referirse a un único parámetro en una lambda cuando solo se tiene un parámetro. Es útil cuando la lambda solo toma un parámetro y no se especifica un nombre para ese parámetro.

fun calcula(x: Int,op: (Int)->Int):Int{
    return op(x)
}

fun main() {
    val cua: (Int) -> Int = { it * it }
    println(calcula(4,cua))

    val numeros = listOf(1, 2, 3, 4, 5)
    println(numeros)

    val numerosPares = numeros.filter { it % 2 == 0 }
    println(numerosPares)
}

Aquí tienes un ejemplo que ilustra el acceso a variables externas mutables

fun main() {
    var contador = 0
    val incrementar: () -> Unit = { contador++ }
    
    incrementar()
    println(contador) // Imprime 1
}

4. Alias (typealias)

typealias se utiliza para proporcionar un nombre alternativo o alias a un tipo existente

typealias EnteroDeDosBytes = Short

fun main() {
    val numero: EnteroDeDosBytes = 32767
    println(numero) // Imprime 32767
}

5. Desestructuración

La desestructuración es una característica en Kotlin que permite descomponer los objetos en sus componentes individuales y asignarlos a variables individuales.

data class Persona(val nombre: String, val edad: Int)

fun main() {
    val persona = Persona("Juan", 30)
    val (nombre, edad) = persona // Desestructuración

    println("Nombre: $nombre")
    println("Edad: $edad")
}

6. Try…Catch

fun main() {
    try {
        // Código que puede generar una excepción
        val resultado = dividir(10, 0)
        println("Resultado: $resultado")
    } catch (e: ArithmeticException) {
        // Captura la excepción específica
        println("Error: ${e.message}")
    } finally {
        // Código que se ejecuta siempre, independientemente de si hay una excepción o no
        println("Operación finalizada")
    }
}

fun dividir(a: Int, b: Int): Int {
    return a / b
}
fun main() {
    try {
        dividir(10, 0)
    } catch (e: ArithmeticException) {
        println("Error: ${e.message}")
    }
}

fun dividir(a: Int, b: Int): Int {
    if (b == 0) {
        throw ArithmeticException("División por cero no permitida")
    }
    return a / b
}
class ErrorMio(msg:String):Exception(msg)

    fun main() {
        try {
            dividir(10, 0)
        } catch (e: ErrorMio) {
            println("Error: ${e.message}")
        }
    }

    fun dividir(a: Int, b: Int): Int {
        if (b == 0) {
            throw ErrorMio("División por cero no permitida")
        }
        return a / b
    }

7. Scope Functions

data class Persona(var nombre: String, var edad: Int)

 fun main() {
        val persona = Persona("John Doe", 30)

        // Ejemplo de uso de let
        val resultadoLet = persona.let {
            it.nombre = it.nombre.uppercase()
            it.edad += 1
            it.edad * 2
        }
        println("Resultado let: $resultadoLet") //Resultado let: 62
        println("Persona: $persona") //Persona: Persona(nombre=JOHN DOE, edad=31)

        val persona2 = Persona("John Doe", 30)
        // Ejemplo de uso de apply
        val resultadoApply = persona2.apply {
            nombre = nombre.uppercase()
            edad += 1
        }
        println("Resultado apply: $resultadoApply") //Resultado apply: Persona(nombre=JOHN DOE, edad=31)
        println("Persona2: $persona2") //Persona2: Persona(nombre=JOHN DOE, edad=31)
    }

8. Operador Elvis

El operador Elvis en Kotlin es representado por ?:. Se utiliza para proporcionar un valor predeterminado o alternativo en caso de que una expresión sea nula.

 fun main() {
        val nombre: String? = null
        val nombrel = nombre?.length ?: 0
        println("Longitud del nombre: $nombrel") //Longitud del nombre: 0


        val apellido: String? = "Gómez"
        val apellidol = apellido?.length ?: 0
        println("Longitud del apellido: $apellidol") //Longitud del apellido: 5
    }

9. Lateinit y Lazy

Tanto lateinit como lazy son mecanismos que te permiten diferir la inicialización de propiedades hasta que sean necesarias.

lateinit:

Se utiliza cuando sabes que una propiedad será inicializada antes de su primer uso y quieres evitar la necesidad de asignar un valor inicial inmediato o utilizar un modificador nullable (var prop: Tipo?).

class MiClase {
    lateinit var nombre: String

    fun inicializarNombre() {
        nombre = "John Doe"
    }

    fun imprimirNombre() {
        println(nombre)
    }
}

fun main() {
    val objeto = MiClase()
    objeto.inicializarNombre()
    objeto.imprimirNombre()
}

lazy:

Se utiliza cuando deseas inicializar una propiedad perezosamente, es decir, su valor se calcula solo cuando se accede a ella por primera vez.

val miPropiedad: String by lazy {
    println("Calculando valor...")
    "Mi valor"
}

fun main() {
    println(miPropiedad)
    println(miPropiedad)
}

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *