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)
}