Generalidades

Genéricos en Kotlin. ¿Qué es? | Por Saravana Thiyagaraj | Maderaps | Septiembre de 2021

Saravana Tiagaraghi

¿Qué es?

Estos son genéricos: List<String>, List<Any>, Map<Int, String>, Jungle<T>, y muchos más…

que es T existe Jungle<T>?

T Es de cualquier tipo.Trátelo como un marcador de posición al definir Jungle<T> igual que:

class Jungle<T> 
fun obtainType(): T
// ...

Entonces, ¿cuál es la diferencia entre los dos Jungle<T> con Jungle<Animal>?

T Es un marcador de posición y puede ser reemplazado por cualquier tipo en el proceso de implementación. Animal Es uno de ellos. Es como:

fun main() 
val jungle: Jungle<Animal> = Jungle()

Dicen «A diferencia de Array, ArrayList es inmutable». ¿Qué quiere decir esto?

Invariante Es una especie de variación, con Covarianza con Contravariante. P.ej, String Sí subtipo Any. pero ArrayList<String> Subtipo que no es ArrayList<Any>. Se llama Invariable.
si T Es un subtipo U, Luego:

  • Invariante : si Jungle<T> Subtipo que no es Jungle<U>
  • Covarianza : si Jungle<T> Es un subtipo Jungle<U>
  • Inversor : si Jungle<T> Es un supertipo Jungle<U>

Lo siguiente es imposible y provocará un error de compilación porque ArrayList No cambia:

fun main() 
val strings: ArrayList<String> = ArrayList()
val objects: ArrayList<Any> = strings // Compile error

Espere. ¿Por qué arroja un error de compilación? ¿Qué tiene de malo la conversión de tipos como esta?

Extendamos un poco el código y verá por qué no está permitido. Supongamos que el compilador no arrojará un error durante la conversión de tipos.Esto resultará en el siguiente error de tiempo de ejecución (porque lastItem No es una cuerda, pero Int):

fun main() 
val strings: ArrayList<String> = ArrayList()
strings.add("One")
val objects: ArrayList<Any> = strings // Assuming no compile error
objects.add(5)
val lastItem: String = strings.last() // Runtime error

comprendido. Pero si este es el caso, entonces todos los tipos genéricos deberían ser Invariantes, ¿verdad? ¿Cómo es posible la covarianza y la contravarianza?

Puedes hacer un tipo Covarianza o Inversor Usando llamado Declarar las diferencias del sitioPero antes de eso, necesita comprender algunas cosas importantes.La razón por la que el ejemplo anterior arroja un «error de tiempo de ejecución» es que podemos agregar Int. Si tu no tienes add Método, entonces este problema no debería ocurrir.O más claramente, si no permite Escribir Operaciones sobre tipos, pero solo leer Operación, entonces podemos usarlo de manera segura como un tipo covariante.

Piénsalo.Si tienes uno ArrayList<String> Pero no puede agregarle nada (sin operación de escritura), entonces puede asegurarse de que este ArrayList siempre contenga solo cadenas.Esto significa que no solo puede leer con seguridad String A partir de él, también puede leerlo como AnyYa que Any Sí supertipo StringEsto solo es posible porque está impidiendo que la operación de escritura escriba T. Esta es la lógica detrás de declarar las diferencias de sitios.Entonces, para hacer nuestro Jungle<T> Como covarianza, usamos out como sigue:

class Jungle<out T>(val t: T) 
fun obtainType(): T
return t

// fun insertType(t: T) // Not Possible (Type parameter T is declared as 'out', so no Write operation on type T)

fun main()
val jungleWithAnimals: Jungle<Animal> = Jungle(Cat())
val jungleWithAnything: Jungle<Any> = jungleWithAnimals // This won't be possible if you remove the `out`

Así que aquí básicamente le estamos diciendo al compilador Jungle Solo permitido Read funcionar T mediante el uso out Palabras clave.Ahora puedes escribir Jungle<Animal> llegar Jungle<Any>. esto no es nada Covarianza.
Del mismo modo, puede hacer Jungle Inversor usar in. Esto es exactamente lo contrario de lo que estamos hablando ahora, donde Escribir Está permitido, pero leer prohibido.

Vaya, eso tiene sentido.Acabo de comprobar el código fuente List En Kotlin, en realidad se define como List<out E>. asi que List Es covariante, pero MutableList No ha cambiado. Pero, ¿y si no tenemos una clase de comportamiento covariante?Digamos Jungle<T> Está definida como una biblioteca de terceros y queremos usarla como un tipo covariante, pero no podemos cambiar el código fuente. ¿Cómo es eso posible?

Entonces esto nos deja Usar diferencia de ubicación Llama Proyección de tipo. Tu (nosotros) podemos hacerlo Jungle<T> Covarianza En el tipo T, al usarlo, se ve así:

fun main() 
val jungleWithAnimals: Jungle<Animal> = Jungle(Cat())
val jungleWithAnything: Jungle<out Any> = jungleWithAnimals
// val type: Animal = jungleWithAnything.obtainType() // Not possible. You can only Read 'Any'
val type: Any = jungleWithAnything.obtainType()
// jungleWithAnything.insertType(Cat()) // Not possible. You can't Write.

Presta atención al uso out aquí.Este para ti Covarianza Restringiéndote a escribir operaciones.Además, dado que el tipo aquí es Any, Solo puedes leer como Any En lugar de como Animal. Como siempre, lo mismo se aplica a in al igual que.Puede ayudarlo a usar tipos como Inversor.

Oh ya veo.Estaba navegando por un código y encontré algo similar Jungle<T : Animal>. ¿Qué significa?Creo que significa T No solo el de cualquier tipo, sino que debe ser Animal. ¿Esto es real?

Estás absolutamente en lo correcto.Aquí establecemos un límite superior para el tipo T, O en otras obras T Debería ser un subtipo Animal.

¡No está mal, genial! Podemos tener un tipo más, por ejemplo, Jungle<T,U,V> ?

Sí tu puedes. Puede tener cualquier número, pero un buen diseño es limitarlo a 2. Más solo provocará confusión.Además, tenga en cuenta in con out Puede aplicarse de forma independiente a este tipo de variables.Entonces puedes tener algo como Jungle<in T, out U, in V : Animal>. Por eso cuando hablas de Covarianza o Inversor, También debe mencionar las variables de tipo. aquí, JungleCovarianza existe U, pero Contravariante existe T con V.

F out, in con : Establece un límite superior.que es Jungle<*> ¿Luego? Estoy confundido.

Tu usas * Supongamos que no conoce el tipo, pero aún desea usarlo de manera segura.Estos se llaman Proyección de estrellas. Suponga que tiene un List Cosa, no sabes de qué tipo es, y luego usas List<*>Esto tiene diferentes significados basados ​​en la varianza.Si solo puedes leer, entonces * Significa que puedes leer Any?, Si solo puedes escribir, significa que puedes escribir Nothing. Piénsalo.Si no sabe nada sobre Type, solo puede leer los tipos básicos de todas las extensiones de Type Any?. Cuando tienes que escribir, no puedes escribir nada porque no conoces el tipo. Las siguientes son situaciones diferentes basadas en la variación:

  • Jungle<out T: Animal> : aquí Jungle<*> método Jungle<out Animal>, Es decir, puedes leer Animal De Jungle<*>
  • Jungle<in T>: Jungle<*> método Jungle<in Nothing>, Es decir, no puedes escribir nada Jungle<*>
  • Jungle<T: Animal> : aquí Jungle<*> método Jungle<out Animal> Al leer y Jungle<in Nothing> Cuando se escribe.
  • Jungle<T> : aquí Jungle<*> método Jungle<out Any?> Al leer y Jungle<in Nothing> Cuando se escribe.

En el caso de varios tipos de variables, puede utilizar * Para cualquier tipo de variable.Por ejemplo, para Jungle<in T, out U, in V : Animal>, Un posible caso de proyección de estrellas puede ser Jungle<*, Int, *>.

Oye, escuché algo sobre where Palabras clave. ¿Así es como ocurre?

Entonces viste el uso de : Cuando desee establecer un límite superior.Suponga que tiene que establecer varios límites superiores para el mismo tipo de variable, por ejemplo Jungle<T> con T Debería ser un subtipo Animal con WalkableY luego usa where:

class Jungle<T>(var t: T) where T: Animal, T: Walkable 
fun obtainType(): T = t
fun insertType(t: T)

Eso es interesante. ¿Qué pasa con las funciones genéricas? Tiene alguna idea sobre esto?

Sí, incluso si no usa tipos genéricos en clases o interfaces, puede aplicarlos directamente a las funciones.No puedes usar in con out, Pero puede establecer el límite superior de la variable de tipo : o whereIncluso puede tener varias variables de tipo.

fun <T> getAnimals(t: T): List<T> where T: Animal, T: Walkable 
return emptyList()

Wow, muchas gracias. Ahora todas mis dudas están resueltas. ¿Me estoy perdiendo algo en los genéricos?

Tener un concepto Borrado de tipo con Parámetros de tipo reificadoPero creo que este es otro día. ¡Mantente a salvo y vuélvete loco!

LEER  Val Kilmer ha podido hablar en Top Gun: Maverick gracias a la inteligencia artificial

Publicaciones relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Botón volver arriba