← Kotlin EspañolChapter 12 of 13

Funciones de Extensión

## Objetivos de Aprendizaje - Comprender funciones de extensión - Aprender propiedades de extensión - Trabajar con extensiones de objeto complementario - Dominar conceptos básicos de genéricos - Usar restricciones genéricas ## Conceptos Básicos de Funciones de Extensión Las funciones de extensión añaden funciones a clases existentes sin modificarlas: ```kotlin fun String.addExclamation(): String = this + "!" fun main() { val greeting = "Hello".addExclamation() println(greeting) // Hello! } ``` ### ¿Por qué Funciones de Extensión? ```kotlin // Without extension fun lastChar(str: String): Char = str[str.length - 1] // With extension fun String.lastChar(): Char = this[length - 1] fun main() { println(lastChar("Kotlin")) // n println("Kotlin".lastChar()) // n } ``` ## Sintaxis de Extensión ```text fun ClassName.functionName(params): ReturnType { // this refers to ClassName instance } ``` ```kotlin fun Int.isEven(): Boolean = this % 2 == 0 fun String.addNumbers(num: Int): String = this + num fun main() { println(4.isEven()) // true println(5.isEven()) // false println("Answer: ".addNumbers(42)) // Answer: 42 } ``` ## Receptor Anulable Las extensiones pueden ser llamadas en tipos anulables: ```kotlin fun String?.orEmpty(): String = this ?: "" fun main() { val nullStr: String? = null println(nullStr.orEmpty()) // "" val normalStr: String? = "Hello" println(normalStr.orEmpty()) // Hello } ``` ## Propiedades de Extensión Añadir propiedades a clases existentes: ```kotlin val String.uppercaseFirst: String get() = if (isEmpty()) "" else this[0].uppercase() + substring(1) var MutableList.lastIndex: Int get() = size - 1 set(value) { /* Not common for immutable */ } fun main() { println("hello".uppercaseFirst) // Hello val list = mutableListOf(1, 2, 3) println(list.lastIndex) // 2 } ``` ## Alcance de las Extensiones ### Extensiones Regulares ```kotlin package mypackage fun String.addExclamation() = this + "!" // Can only be used when imported ``` ### Extensiones como Miembros ```kotlin class MyClass { fun String.extended() = "Extended: $this" } ``` ## Extensiones de Objeto Complementario ```kotlin class MyClass { companion object { // Fields only } // Extension on companion object fun MyClass.Companion.create(): MyClass = MyClass() } // Usage fun main() { val obj = MyClass.create() } ``` ### Ejemplo Más Común ```kotlin class Person private constructor(val name: String) { companion object { fun fromName(name: String): Person = Person(name) } } fun main() { val person = Person.fromName("Alice") println(person.name) // Alice } ``` ## Genéricos ### Funciones Genéricas ```kotlin fun List.firstOrNull(): T? = if (isEmpty()) null else this[0] fun printItem(item: T) { println(item) } fun main() { println(listOf(1, 2, 3).firstOrNull()) // 1 println(emptyList().firstOrNull()) // null printItem("Hello") // Hello printItem(42) // 42 } ``` ### Clases Genéricas ```kotlin class Box(val value: T) { fun getValue(): T = value } class Pair(val first: A, val second: B) fun main() { val intBox = Box(42) val stringBox = Box("Hello") println(intBox.getValue()) // 42 println(stringBox.getValue()) // Hello val pair = Pair(1, "one") println("${pair.first}, ${pair.second}") // 1, one } ``` ### Restricciones Genéricas ```kotlin // Upper bound - T must be Comparable fun > List.maxOrNull(): T? { if (isEmpty()) return null return sortedDescending().first() } // Multiple bounds fun List.joinToString( separator: String = ", " ): String where T : CharSequence, T : Comparable { return this.joinToString(separator) } fun main() { println(listOf(1, 5, 3, 2, 4).maxOrNull()) // 5 val strings = listOf("apple", "cherry", "banana") println(strings.maxOrNull()) // cherry } ``` ## Varianza ### Invarianza ```kotlin // List is invariant class Container // Container is NOT a subtype of Container ``` ### Covarianza (out) Productor solo - solo puede producir, no consumir: ```kotlin class Producer { fun produce(): T = TODO() } // Producer IS a subtype of Producer val intProducer: Producer = TODO() val numProducer: Producer = intProducer // OK ``` ### Contravarianza (in) Consumidor solo - solo puede consumir, no producir: ```kotlin class Consumer { fun consume(item: T) {} } // Consumer IS a subtype of Consumer val numConsumer: Consumer = TODO() val intConsumer: Consumer = numConsumer // OK ``` ## Erasura de Tipos La información de tipo genérico se borra en tiempo de ejecución: ```kotlin fun main() { val list1: List = listOf("a", "b") val list2: List = listOf(1, 2) // At runtime, both are just List println(list1 is List<*>) // true println(list2 is List<*>) // true // Cannot do this: // if (list is List) // Error: Cannot check specific type } ``` ### Tipos Reificados Usa `reified` para hacer el tipo disponible en tiempo de ejecución (solo funciones inline): ```kotlin import kotlin.reflect.typeOf inline fun printType() { println(typeOf()) } fun main() { printType() // kotlin.String printType() // kotlin.Int printType>() // kotlin.collections.List } ``` ## Cláusulas Where ```kotlin interface Readable interface Writable class Data where T : Readable, T : Writable { fun read(): T = TODO() fun write(item: T) {} } ``` ## Resumen - Las funciones de extensión añaden métodos a clases existentes - Sintaxis: `fun ClassName.functionName() = ...` - Las propiedades de extensión añaden propiedades a clases existentes - Extensiones con receptor anulable (`String?`) - Funciones genéricas usan `` antes del nombre de función - Clases genéricas: `class Box(val value: T)` - Upper bound: `>` - Covarianza: `out T`, Contravarianza: `in T` - La erasure de tipos elimina genéricos en tiempo de ejecución - `reified` preserva el tipo genérico en funciones inline

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →