Varianza en el punto de declaración
in y out
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.
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
- Funciones y clases genéricas
- Varianza en el punto de declaración
- Varianza en el punto de uso
- Restricciones de genéricos