Kotlin Academy · Lección

Varianza en el punto de declaración

in y out

Lección 2 de 413 pasos

Varianza en el punto de declaración es una lección gratuita de Kotlin Academy en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Kotlin Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Kotlin Academy incluye 4 lecciones en total.

El problema de la varianza

¿Es List<String> un tipo de List<Any>? A veces sí y a veces no. La varianza describe cómo se relacionan los tipos genéricos cuando se relacionan sus parámetros de tipo. Kotlin utiliza out y in para expresarlo.

Invariancia por defecto

De forma predeterminada, los genéricos son invariantes: Box<String> NO es un Box<Any>, aunque String sea un subtipo de Any. Esto evita operaciones no seguras.

class Box<T>(val value: T)

fun main() {
    val strBox = Box("hi")
    // val anyBox: Box<Any> = strBox // would not compile
    println(strBox.value)
}

Covarianza con out

Marcar un parámetro de tipo como out lo hace covariante: Producer<String> se convierte en un subtipo de Producer<Any>. El tipo solo puede aparecer en posiciones de salida, como los tipos de retorno.

class Producer<out T>(private val item: T) {
    fun produce(): T = item
}

fun main() {
    val strProducer: Producer<String> = Producer("hello")
    val anyProducer: Producer<Any> = strProducer
    println(anyProducer.produce())
}

Por qué out es seguro

Si una clase solo produce, es decir, devuelve, T y nunca lo consume, tratar un Producer<String> como un Producer<Any> es seguro, porque cada String que devuelve también es un Any.

out en la biblioteca estándar

El List<out E> de solo lectura de Kotlin es covariante, por lo que se puede utilizar un List<String> donde se espera un List<Any>.

fun printAll(items: List<Any>) {
    items.forEach { println(it) }
}

fun main() {
    val names: List<String> = listOf("Ann", "Bob")
    printAll(names)
}

Contravarianza con in

Marcar un parámetro de tipo como in lo hace contravariante: Consumer<Any> se convierte en un subtipo de Consumer<String>. El tipo solo puede aparecer en posiciones de entrada, como los parámetros.

class Consumer<in T> {
    fun consume(item: T) {
        println("Consumed $item")
    }
}

fun main() {
    val anyConsumer: Consumer<Any> = Consumer()
    val strConsumer: Consumer<String> = anyConsumer
    strConsumer.consume("hi")
}

Por qué in es seguro

Un consumidor que acepta Any sin duda puede aceptar un String. Por tanto, un Consumer<Any> puede utilizarse en cualquier lugar donde se necesite un Consumer<String>.

La mnemotecnia PECS

Recuerde Producer Extends, Consumer Super o, en términos de Kotlin: out para productores, es decir, salida, e in para consumidores, es decir, entrada. Un tipo que solo produce T utiliza out; uno que solo recibe T utiliza in.

Combinar in y out

Un parámetro de tipo utilizado tanto para la entrada como para la salida, como en una lista mutable, debe seguir siendo invariante. Los tipos Function muestran ambas direcciones: los parámetros son in y el valor de retorno es out.

fun main() {
    val f: (Number) -> Int = { it.toInt() }
    val g: (Int) -> Number = f
    println(g(3.0.let { Number::class; 5 }))
}

El sitio de declaración significa un solo lugar

La varianza en el sitio de declaración se indica una sola vez, en la propia clase, con in/out. Cada uso posterior de la clase obtiene automáticamente esa varianza, a diferencia de Java, donde se repiten los comodines en cada uso.

Elegir in u out

Pregúntese: ¿esta clase solo produce T, por lo que debe utilizar out; solo consume T, por lo que debe utilizar in; o hace ambas cosas, por lo que debe dejarla invariante? Hacer coincidir el modificador con la función del tipo permite que sus tipos genéricos sean flexibles y seguros.

Comprobación rápida

¿Qué indica el modificador out?

Resumen

Los genéricos son invariantes de forma predeterminada. Utilice out para la covarianza cuando un tipo solo produzca valores, y in para la contravarianza cuando solo consuma valores. Recuerde: los productores utilizan out y los consumidores, in. A continuación, aplicará la varianza en el punto de uso mediante proyecciones de tipos.

Gratis para empezar

Aprende Kotlin con un tutor de IA — gratis

Escribe y ejecuta código real en tu navegador, obtén ayuda instantánea de un tutor de IA disponible 24/7 y continúa donde lo dejaste en la web o en la aplicación.

Cursos
51
Lecciones
203

Preguntas frecuentes

¿La lección «Varianza en el punto de declaración» es gratis?

Sí — el texto completo de «Varianza en el punto de declaración» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Kotlin Academy, actualiza a CoddyKit PRO. El curso de Kotlin Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Varianza en el punto de declaración»?

in y out Practicas Kotlin Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Kotlin Academy?

No se requiere experiencia previa. Kotlin Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.

¿Cuánto tiempo toma la lección «Varianza en el punto de declaración»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Kotlin Academy?

Sí. Cada lección de Kotlin Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Funciones y clases genéricas
  2. Varianza en el punto de declaración
  3. Varianza en el punto de uso
  4. Restricciones de genéricos
← Volver a Kotlin Academy