Generics for getting started with kotlin

[Start learning from code] Generics in Kotlin

Before learning kotlin generics, let's review the basics of Java generics.

Speaking of generics, we may be most commonly used in the three major collections.

generic

Generalize the specific type, use symbols to replace the type when encoding, and then determine its type when using it.

Because of the existence of generics, we can save the cast.

Generics are related to types, so can we also use polymorphism with types?

scene one:

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

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

Why List<TextView> textViews=buttons;is it reporting an error? This is because Java's generics
are inherently immutable . In Java, it will be considered inconsistent List<TextView>with the type, that is, the generic type ( ) of the subclass does not belong to the subclass of the generic type ( ).List<Button>
List<Button>List<TextView>

Java's generic types will undergo type erasure at compile time . In order to ensure type safety, such assignment is not allowed.
As for what type erasure is, we will talk about it later.

In actual use, we will indeed use this similar requirement and need to implement the above assignment.
Java has also thought of it, so it provides us with generic wildcards ? exntends and ? super
to solve this problem

Correct understanding of Java generics ? exntendsand? super

? exntends

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

This ? extendsis called 上界通配符, let Java generics have covariance , covariance is to allow the above
assignment to be legal.

It has two meanings:

  1. Among them ?is a wildcard, indicating that Listthe generic type of this is an unknown type
  2. extendsRestricts the upper bound of this unknown type, that is, the generic type must satisfy extendsthis
    restriction

This is a bit different from the defined keyword class:extends

  • Its scope is not only all direct or indirect subclasses, but also the parent class itself defined by the upper bound, that is,
    TextView
  • It also implementsmeans that the upper bound here can also be interface.

Here Buttonis TextViewa subclass of , so it meets the constraints of the generic type, so it can be successfully
assigned.

The following situations can also be assigned successfully.

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

The general collection class contains two operations of getand , such as in .addJavaList

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

The symbols in the above code Ethat represent generic types.

ListLet's see if there is any problem with the use of the upper bound wildcard :

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

As mentioned earlier, the generic type of List<? extends TextView> is an unknown type? The compiler is not sure
what type it is, but there is a restriction.

? extends TextViewDue to the constraints it satisfies , getthe object that comes out must be TextView
a subclass of . According to the characteristics of polymorphism, it can be assigned to TextView.

When it comes to addoperation, we can understand it as:

  • List<? extends TextView>Since the type is unknown, it could be a List, or a
    List<TextView>, List<RadioButton>.
  • For the former, TextViewit is obviously not possible for us to add
  • The reality is that the compiler cannot determine which one it belongs to. If it cannot continue to execute, it will report an error.

You might be thinking then why am I using wildcards ??

Actually, List<?>the equivalent List<? extends Object>of the abbreviation.

Due to the limitation of add, those who use ? extendsgeneric wildcards Listcan only provide data for consumption. From this perspective, the party that provides data is called "Producer". Correspondingly, there is another concept called "Consumer", which corresponds to another generic wildcard in Java? super.

? super

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

This ? superis called the lower bound wildcard , which can make javagenerics contradictory

There are two meanings:

  • ?The generic type represented by the wildcard Listis an unknown type
  • superRestricts the lower bound of this unknown type, that is, the generic type must satisfy this super
    restriction
    • superWe often use it in class methods. The scope here includes not only Buttonthe direct and indirect
      parent classes, but also Buttonitself
    • superAlso supported interface.

According to the above example, TextViewif Buttonthe row is the parent type, it can also meet superthe restriction conditions, and the
value can be assigned successfully.

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

For those that use lower bound wildcards List, we're looking at getand addmanipulating:

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

To explain, first of all? means an unknown type, and the compiler is not sure of its type.

Although I don't know its specific type, any object in Java is a subclass of Object, so it can be assigned to Object here.

The Button object must be a subtype of this unknown type. According to the characteristics of polymorphism, it is legal to add the Button object through add here.

Use the lower bound wildcard? The generic List of super can only read Object objects. Generally, there is no actual usage scenario. Usually,
it is only used to add data, that is, to consume the existing List<? super Button>, and enter it Add Button,
so this generic type declaration is called "Consumer Consumer".

To sum up, Java's generics themselves do not support covariance and contravariance.

Generic wildcards can be used ? extendsto enable generics to support covariance, but "can only be read and cannot be modified",
the modification here only refers to adding elements to the generic collection, if it is remove(int index)and clearof course it is possible.

Generic wildcards can be used ? superto enable generics to support inversion, but "can only be modified and cannot be read". The non-
readable here means that you cannot read according to the generic type. If you Objectread it out and then force it, of course you can of.

After talking about generics in Java, let's look back at generics in kotlin.

outand in kotlinin

kotlinLike javagenerics, kotlingenerics in are themselves immutable.
- Use keywords outto support covariance, equivalent to Javaupper bound wildcards in ? extends
- Use keywords into support contravariance, equivalent to Javaupper bound wildcards in? super

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

outIndicates that my variable or parameter can only be used for output, not input, you can only read me, not write me;

inMeans: I am only used for input, not output, you can only write me, not read me.

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()
}
outSubsection:
  • subclass Derivedcompatible parentBase
  • producer Producer<Derived>compatibleProducer<Base>
  • Generic parameters of classes with covariant points must be declared as covariant or invariant
  • Use covariance when the generic class acts as a producer of instances of the generic parameter class

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)
}

inSubsection:
  • subclass Derivedcompatible parentBase
  • consumer Producer<Derived>compatibleProducer<Base>
  • Generic parameters of classes with contravariant points must be declared as covariant or invariant
  • Use covariance when generic classes act as consumers of instances of generic parameter classes

*Number

*Number
As mentioned earlier, a single ?number in Java can also be used as a generic wildcard, which is equivalent to ? extends Object.
It has an equivalent in Kotlin: *number, equivalent out Any.

var list: List<*>

outThe difference from Java is that if you already have an or in your type definition in,
this restriction will still exist when the variable is declared, and will not be *removed by the number.

For example, it is in your type definition out T : Number, then <*>the effect after it is added is not out Any,
but out Number.

Example:
Covariation point example

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

Inversion point example

class Function<in P1,in P2>{
    
    
    fun invoke(p1: P1,p2: P2)=TODO()
}
val f:Function<*,*>=Function<Number,Any>()
f.invoke()//参数为下限,但是我们的kotlin中下限为`Nothing`,无法实例化。所以该方法的参数是传入不了的
*rule
  • If it is outused in the generic type of the modified class, then its upper limit will be taken
  • If it is inused in the generic type of the modified class, then its lower limit will be takenNothing
*scope of use
  • *Cannot be applied directly or indirectly to properties or functions

    • Wrong way:
    • QueryMap<String,*>()
    • maxOf<*>(1,3)
  • *Suitable for use in scenarios described as type

    • val querMap:QueryMap<*,*>
    • if(f is Function<*,*>){...}
    • HashMap<String,List<*>>(), Note: List<*> here is actually valuea generic parameter

    Generic concept

1. Generics are an abstraction at the type level

2. Generics realize the ability to construct more general types through generic parameters

3. Generics allow types that conform to the inheritance relationship to implement certain capabilities in batches

generic class

    class List<T> {
    
    }

generic method

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

Generic constraints

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

multiple generic constraints ( where)

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

multiple generic parameters

    //该函数返回类型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()
    }

inline specialization

We need to know the principle of generics when explaining inline specialization.

  • Pseudo-generic: type erasure at compile time, no actual type generation at runtime
    • For example: javakotlin
  • True generics: real types are generated at compile time and exist at runtime
    • For example: C#,C++

We know that generics on the JVM are generally implemented through type erasure, so they are also called pseudo-generics, which means that type arguments are not saved at runtime.

In fact, we can declare an inline function so that its type arguments are not erased, but this is not possible in 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>()
 }

practical application

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>(""" {...}""") //泛型参数

Guess you like

Origin blog.csdn.net/weixin_44710164/article/details/108632281