Genéricos para empezar con kotlin

[Empiece a aprender del código] Genéricos en Kotlin

Antes de aprender los genéricos de Kotlin, repasemos los conceptos básicos de los genéricos de Java.

Hablando de genéricos, es posible que los que más utilizamos sean las tres colecciones principales.

genérico

Generalice el tipo específico, use símbolos para reemplazar el tipo al codificar y luego determine su tipo al usarlo.

Gracias a la existencia de genéricos, podemos salvar el elenco.

Los genéricos están relacionados con los tipos, entonces, ¿podemos usar también el polimorfismo con los tipos?

escena uno:

//多态,因为Button是TextView的子类,向上转型
TextView textView=new Button(context);  

List<Button> buttons=new ArrayList<Button>();
//当我们将多态使用到这里时,就发生错误。
List<TextView> textViews=buttons;

¿ Por qué List<TextView> textViews=buttons;informa un error? Esto se debe a que los genéricos de Java
son inherentemente inmutables . En Java, se considerará inconsistente List<TextView>con el tipo, es decir, el tipo genérico ( ) de la subclase no pertenece a la subclase del tipo genérico ().List<Button>
List<Button>List<TextView>

Los tipos genéricos de Java se borrarán en el momento de la compilación . Para garantizar la seguridad de los tipos, dicha asignación no está permitida.
En cuanto a qué es el borrado de tipos, hablaremos de ello más adelante.

En el uso real, utilizaremos este requisito similar y necesitaremos implementar la tarea anterior.
Java también ha pensado en ello, por eso nos proporciona comodines genéricos ? exntends y ? super
para solucionar este problema.

Comprensión correcta de los genéricos de Java ? exntendsy? super

? exntends

    List<Button> buttons=new ArrayList<Button>();
    List<? extends TextView> textViews=buttons;
    

Esto ? extendsse llama 上界通配符dejar que los genéricos de Java tengan covarianza . La covarianza es para permitir que la
asignación anterior sea legal.

Tiene dos significados:

  1. Entre ellos ?hay un comodín, que indica que Listel tipo genérico de este es un tipo desconocido.
  2. extendsRestringe el límite superior de este tipo desconocido, es decir, el tipo genérico debe cumplir extendsesta
    restricción.

Esto es un poco diferente de la palabra clave definida class:extends

  • Su alcance no es solo todas las subclases directas o indirectas, sino también la propia clase principal definida por el límite superior, es decir,
    TextView
  • También implementssignifica que el límite superior aquí también puede ser interface.

Aquí Buttonhay TextViewuna subclase de , por lo que cumple con las restricciones del tipo genérico, por lo que se puede
asignar con éxito.

Las siguientes situaciones también se pueden asignar con éxito.

List<? extends TextView> textViews=new ArrayList<TextView>(); //本身
List<? extends TextView> textViews=new ArrayList<Button>(); //直接子类
List<? extends TextView> textViews=new ArrayList<RadioButton>(); //间接子类

La clase de colección general contiene dos operaciones de gety , como en .addJavaList

public interface List<E> extends Collection<E>{
    
    
    E get(int index);
    boolean add(E e);
}

Los símbolos en el código anterior Eque representan tipos genéricos.

ListVeamos si hay algún problema con el uso del comodín de límite superior :

List<? extends TextView> textViews=new ArrayList<Button>();
TextView textView=textViews.get(0);//get方法可以使用
textViews.add(textView);//add会报错

Como se mencionó anteriormente, ¿el tipo genérico de List<? extends TextView> es un tipo desconocido? El compilador no está seguro de
qué tipo es, pero existe una restricción.

? extends TextViewDebido a las restricciones que satisface , getel objeto que sale debe ser TextView
una subclase de . Según las características del polimorfismo, se puede asignar a TextView.

En cuanto a su addfuncionamiento, podemos entenderlo como:

  • List<? extends TextView>Como se desconoce el tipo, podría ser una Lista o un
    List<TextView>, List<RadioButton>.
  • Para el primero, TextViewobviamente no nos es posible agregar
  • La realidad es que el compilador no puede determinar a cuál pertenece. Si no puede continuar ejecutándose, informará un error.

Quizás estés pensando entonces ¿por qué estoy usando comodines ??

En realidad, List<?>el equivalente List<? extends Object>de la abreviatura.

Debido a la limitación de add, quienes utilizan ? extendscomodines genéricos Listsolo pueden proporcionar datos para consumo, desde esta perspectiva, quien proporciona datos se denomina "Productor". En consecuencia, existe otro concepto llamado "Consumidor", que corresponde a otro comodín genérico en Java: super.

? super

List<? super Button> buttons=new ArrayList<TextView>()

Esto ? superse llama comodín de límite inferior , lo que puede hacer que javalos genéricos sean contradictorios.

Hay dos significados:

  • ?El tipo genérico representado por el comodín Listes un tipo desconocido
  • superRestringe el límite inferior de este tipo desconocido, es decir, el tipo genérico debe cumplir esta super
    restricción.
    • superA menudo lo usamos en métodos de clase. El alcance aquí incluye no solo Buttonlas
      clases principales directas e indirectas, sino también Buttona sí mismo .
    • superTambién apoyado interface.

Según el ejemplo anterior, TextViewsi Buttonla fila es del tipo principal, también puede cumplir superlas condiciones de restricción y el
valor se puede asignar correctamente.

List<? super Button> buttons=new ArrayList<Button>(); //本身
List<? super Button> buttons=new ArrayList<TextView>(); //直接父类
List<? super Button> buttons=new ArrayList<Object>(); //间接父类

Para aquellos que usan comodines de límite inferior List, estamos analizando gety addmanipulando:

List<? super Button> buttons=new ArrayList<TextView>();
Object object=buttons.get(0); //此处get()可以获取,是因为Object是所有类的父类
Button button =new Button();
buttons.add(button)

Para explicarlo, en primer lugar, significa un tipo desconocido y el compilador no está seguro de su tipo.

Aunque no sé su tipo específico, cualquier objeto en Java es una subclase de Objeto, por lo que se puede asignar a Objeto aquí.

El objeto Botón debe ser un subtipo de este tipo desconocido, según las características del polimorfismo, es legal agregar el objeto Botón mediante add aquí.

¿Utiliza el comodín de límite inferior? La Lista genérica de super solo puede leer objetos Objeto. Generalmente, no existe un escenario de uso real. Por lo general, solo se
usa para agregar datos, es decir, para consumir la Lista existente <? super Botón>, e ingrese el botón Agregar,
por lo que esta declaración de tipo genérico se llama "Consumidor Consumidor".

En resumen, los genéricos de Java en sí no admiten covarianza ni contravarianza.

Se pueden usar comodines genéricos ? extendspara permitir que los genéricos admitan la covarianza, pero "solo se pueden leer y no se pueden modificar",
la modificación aquí solo se refiere a agregar elementos a la colección genérica, si es posible remove(int index)y, clearpor supuesto, es posible.

Se pueden usar comodines genéricos ? superpara permitir que los genéricos admitan la inversión, pero "solo se pueden modificar y no se pueden leer". Lo no
legible aquí significa que no se puede leer de acuerdo con el tipo genérico. Si Objectlo lee y luego lo fuerza, Por supuesto que puedes.

Después de hablar sobre los genéricos en Java, volvamos a los genéricos en Kotlin.

outy en kotlinin

kotlinAl igual que javalos genéricos, kotlinlos genéricos son en sí mismos inmutables.
- use palabras clave outpara respaldar la covarianza, equivalente a Javacomodines de límite superior en ? extends
- use palabras clave inpara respaldar la contravarianza, equivalente a Javacomodines de límite superior en? super

var textViews:List<out TextView>
var textViews:List<in TextView>

outIndica que mi variable o parámetro solo se puede usar para salida, no para entrada, solo puedes leerme, no escribirme;

inSignifica: solo me uso para entrada, no para salida, solo puedes escribirme, no leerme.

out

interface  Book

interface EduBook:Book

class BookStore<out T:Book>{
    
    
    //此处的返回类型,则为协变点
    fun getBook():T{
    
    
        TODO()
    }
}

fun main() {
    
    
    val eduBookStore:BookStore<EduBook> = BookStore<EduBook>()

    val bookStore:BookStore<Book> =eduBookStore
    //我需要书,不管什么类型的,只要是书即可。所以下列两种均可满足
    val book:Book=bookStore.getBook()
    val book1:Book=eduBookStore.getBook()

    var book2:EduBook = eduBookStore.getBook()
    //此处错误,因为已经指定了需要Edu类型的书,你却将book给我。引发错误
    var book4:EduBook = bookStore.getBook()
}
outSubsección:
  • Derivedpadre compatible con subclaseBase
  • Producer<Derived>compatible con el productorProducer<Base>
  • Los parámetros genéricos de clases con puntos covariantes deben declararse como covariantes o invariantes.
  • Utilice covarianza cuando la clase genérica actúa como productor de instancias de la clase de parámetro genérico

in

//垃圾
open class Waste
//干垃圾
class DryWaste : Waste(){
    
    }

//垃圾桶
class Dustbin <in T:Waste>{
    
    
    fun put(t:T){
    
    
        TODO()
    }
}

fun demo(){
    
    
    //创建一个垃圾桶
    val dustbin:Dustbin<Waste> =Dustbin<Waste>()
    //创建一个干垃圾桶
    val dryWasteDustbin:Dustbin<DryWaste> = dustbin

    //垃圾对象
    val waste=Waste()
    //干垃圾对象
    val dryWaste=DryWaste()

    //我们的垃圾桶,可以装我们的垃圾,以及干垃圾
    dustbin.put(waste)
    dustbin.put(dryWaste)

    //而干垃圾桶,只能装干垃圾,所以下面这句话,是错误的。
    dryWasteDustbin.put(waste)
    dryWasteDustbin.put(dryWaste)
}

inSubsección:
  • Derivedpadre compatible con subclaseBase
  • Producer<Derived>compatible con el consumidorProducer<Base>
  • Los parámetros genéricos de clases con puntos contravariantes deben declararse como covariantes o invariantes.
  • Utilice la covarianza cuando las clases genéricas actúen como consumidores de instancias de clases de parámetros genéricos.

*Número

*Número
Como se mencionó anteriormente, un solo ?número en Java también se puede usar como comodín genérico, lo que equivale a ? extends Object.
Tiene un equivalente en Kotlin: *número, equivalente out Any.

var list: List<*>

outLa diferencia con Java es que si ya tiene o en su definición de tipo in,
esta restricción seguirá existiendo cuando se declare la variable y no será *eliminada por el número.

Por ejemplo, está en su definición de tipo out T : Number, entonces <*>el efecto después de agregarlo no es out Any,
sino out Number.

Ejemplo:
ejemplo de punto de covariación

class QueryMap<out K:CharSequence,out V:Any> {
    
    
    fun getKey():K =TODO()
    fun getValue():V =TODO()
}
val queryMap:QuerMap<*,*>= QueryMap<String,Int>()
queryMap.getKey()//类型为CharSequence
queryMap.getValue()//类型为Any

Ejemplo de punto de inversión

class Function<in P1,in P2>{
    
    
    fun invoke(p1: P1,p2: P2)=TODO()
}
val f:Function<*,*>=Function<Number,Any>()
f.invoke()//参数为下限,但是我们的kotlin中下限为`Nothing`,无法实例化。所以该方法的参数是传入不了的
*regla
  • Si se outusa en el tipo genérico de la clase modificada, entonces se tomará su límite superior.
  • Si se inusa en el tipo genérico de la clase modificada, entonces se tomará su límite inferior.Nothing
*alcance de uso
  • *No se puede aplicar directa o indirectamente a propiedades o funciones.

    • Camino equivocado:
    • QueryMap<String,*>()
    • máxDe<*>(1,3)
  • *Adecuado para uso en escenarios descritos como tipo

    • val querMap:QueryMap<*,*>
    • if(f is Function<*,*>){...}
    • HashMap<String,List<*>>(), Nota: List<*> aquí es en realidad valueun parámetro genérico

    Concepto genérico

1. Los genéricos son una abstracción a nivel de tipo.

2. Los genéricos logran la capacidad de construir tipos más generales a través de parámetros genéricos.

3. Los genéricos permiten que los tipos que se ajustan a la relación de herencia implementen ciertas capacidades en lotes.

clase genérica

    class List<T> {
    
    }

método genérico

    fun <T> maxOf(a:T,b:T):T

restricciones genéricas

//表示 T 是Comparable的实现类
fun <T : Comparable<T>> maxOf(a:T,b:T):T{
    
    
    return if(a>b) a else b
}

múltiples restricciones genéricas ( where)

//表示 T 是Comparable的实现类,并且是一个返回值为Unit的方法
 fun <T> callMax(a:T,b:T) where T:Comparable<T>,T:() ->Unit{
    
    
    if (a>b) a() else b()
 }

múltiples parámetros genéricos

    //该函数返回类型R必须继承Number, T 必须实现Comparable 接口,并且是一个返回类型为R的方法
    fun <T,R> callMax(a:T,b:T):R 
            where T:Comparable<T>,T:() -> R,R:Number{
    
    
        return if (a>b) a() else b()
    }

especialización en línea

Necesitamos conocer el principio de los genéricos al explicar la especialización en línea.

  • Pseudogenérico: borrado de tipos en tiempo de compilación, sin generación de tipos reales en tiempo de ejecución
    • Por ejemplo: javakotlin
  • Verdaderos genéricos: los tipos reales se generan en tiempo de compilación y existen en tiempo de ejecución
    • Por ejemplo: C#,C++

Sabemos que los genéricos en la JVM generalmente se implementan mediante el borrado de tipos, por lo que también se denominan pseudogenéricos, lo que significa que los argumentos de tipo no se guardan en tiempo de ejecución.

De hecho, podemos declarar una función en línea para que sus argumentos de tipo no se borren, pero esto no es posible en Java.

 inline fun <reified T> getericMethod(t:T){
    
    
    //此处编译器报错,因为我们即使知道了这个T是什么类型,但不清楚,T有没有无参构造器
    val t=T()
    val ts=Array<T>(3){
    
     TODO()}
    val jclass=T::class.java
    val list=ArrayList<T>()
 }

aplicación práctica

Gson中的
public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
    Object object = fromJson(json, (Type) classOfT);
    return Primitives.wrap(classOfT).cast(object);
  }
  
//我们可以简写为
inline fun <reified T> Gson.fromJson(json:String):T =fromJson(json,T::class.java)

//使用时
val person:Person=gson.fromJson(""" {...}""") //通过类型推导
val person=gson.fromJson<Person>(""" {...}""") //泛型参数

Supongo que te gusta

Origin blog.csdn.net/weixin_44710164/article/details/108632281
Recomendado
Clasificación