当前位置:网站首页>Generics, generic defects and application scenarios that 90% of people don't understand

Generics, generic defects and application scenarios that 90% of people don't understand

2022-07-05 09:52:00 hi-dhl

Author's brief introduction : hi Hello everyone , I am a dhl, Is maintaining its own Personal website , Focus on sharing the latest technology and original articles , involve Kotlin、Jetpack、 Algorithm animation 、 data structure 、 System source code wait .
Reprint note : Not authorized , Prohibited reproduced .

The full text is divided into Video version and Text version ,

  • Text version : Text focuses on detail and depth , Some knowledge , The video is not easy to express , The text description is more accurate
  • Video version : The video will be more intuitive , Read the text version , Watching video , The knowledge points will be clearer

Video version bilibili Address :https://b23.tv/AdLtUGf

Generics are no stranger to every developer , I often see it in the project , But there are many friends , Every time I see wildcards ? extends? superoutin I can't tell the difference between them , And under what circumstances .

Through this article, you will learn the following .

  • Why generics
  • Kotlin and Java The covariance of
  • Kotlin and Java The inverse of
  • wildcard ? extends? superoutin The differences and application scenarios
  • Kotlin and Java The difference of array covariance
  • The defect of array covariance
  • Application scenarios of covariance and inversion

Why generics

stay Java and Kotlin We often use sets in ( ListSetMap wait ) To store data , Various types of data may be stored in the collection , Now we have four data types IntFloatDoubleNumber, Let's say there's no generics , We need to create four collection classes to store the corresponding data .

class IntList{ ...... }
class FloatList{ ...... }
class DoubleList{ ...... }
class NumberList{ ...... }
......
 more 

If there are more types , You need to create more collection classes to store the corresponding data , This shows that it is impossible , Generics are a “ Universal type matcher ”, At the same time, it can make the compiler ensure type safety .

Generics will be concrete types ( IntFloatDouble wait ) Use symbols instead of when declaring , When you use it , To specify a specific type .

//  Use symbols instead of when declaring 
class List<E>{
}

//  stay  Kotlin  Use in , Specify the specific type 
val data1: List<Int> = List()
val data2: List<Float> = List()

//  stay  Java  Use in , Specify the specific type 
List<Integer> data1 = new List();
List<Float> data2 = new List();

Generics help us solve the above problems , But new problems have arisen , We all know IntFloatDouble yes Number subtypes , Therefore, the following code can work normally .

// Kotlin
val number: Number = 1

// Java
Number number = 1;

Let's take three seconds to think , Whether the following code can be compiled normally .

List<Number> numbers = new ArrayList<Integer>();

The answer is no , As shown in the figure below , Compilation error .

This means that generics are immutable ,IDE Think ArrayList<Integer> No List<Number> subtypes , This assignment is not allowed , So how to solve this problem , This requires covariance , Covariance allows the above assignment to be legal .

Kotlin and Java The covariance of

  • stay Java Wildcards are used in ? extends T Represents covariance ,extends The parent type is restricted T, among ? Indicates an unknown type , such as ? extends Number, As long as the type passed in when declaring is Number perhaps Number All subtypes of
  • stay Kotlin Key words in out T Represents covariance , Meaning and Java equally

Now let's modify the above code , Take three seconds to think , Whether the following code can be compiled normally .

// kotlin
val numbers: MutableList<out Number> = ArrayList<Int>()

// Java
List<? extends Number> numbers = new ArrayList<Integer>();

The answer is that you can compile normally , Covariant wildcards ? extends Number perhaps out Number Express acceptance Number perhaps Number The subtype is a collection of objects , Covariance relaxes constraints on data types , But relaxation comes at a price , We were thinking for three seconds , Whether the following code can be compiled normally .

// Koltin
val numbers: MutableList<out Number> = ArrayList<Int>()
numbers.add(1)

// Java
List<? extends Number> numbers = new ArrayList<Integer>();
numbers.add(1)

call add() Method will fail to compile , Although covariance relaxes the constraints on data types , Acceptable Number perhaps Number The subtype is a collection of objects , But at the cost of Unable to add element , You can only get elements , So covariance can only be a producer , Provide data to the outside .

Why can't I add elements

because ? Indicates an unknown type , So the compiler doesn't know what kind of data it will add to the collection , Therefore, it is simply not allowed to add elements to the collection .

But if you want the above code to compile and pass , Want to add elements to the collection , This requires inversion .

Kotlin and Java The inverse of

Inversion actually reverses the inheritance relationship , such as Integer yes Number Subtypes of , however Integer Add the inverse wildcard ,Number yes ? super Integer Subclasses of , As shown in the figure below .

[ Failed to transfer the external chain picture , The origin station may have anti-theft chain mechanism , It is suggested to save the pictures and upload them directly (img-8bRLR2Qd-1655947414076)(https://img.hi-dhl.com/16551339994410.jpg)]

  • stay Java Wildcards are used in ? super T Represent contravariant , among ? Indicates an unknown type ,super Mainly used to restrict subtypes of unknown types T, such as ? super Number, As long as the declaration is passed in Number perhaps Number The parent type of the can
  • stay Kotlin Key words in in T Represent contravariant , Meaning and Java equally

Now let's simply modify the above code , Take three seconds to think about whether you can compile normally .

// Kotlin
val numbers: MutableList<in Number> = ArrayList<Number>()
numbers.add(100)

// Java
List<? super Number> numbers = new ArrayList<Number>();
numbers.add(100);

The answer can be compiled normally , Inverse wildcard ? super Number Or keywords in Reverse the inheritance relationship , Mainly used to restrict subtypes of unknown types , In the example above , The compiler knows that the subtype is Number, So as long as it's Number Subclasses of can be added .

Contravariant can add elements to the set , Can I get the elements ? Let's take three seconds to think about , Whether the following code can be compiled normally .

// Kotlin
val numbers: MutableList<in Number> = ArrayList<Number>()
numbers.add(100)
numbers.get(0)

// Java
List<? super Number> numbers = new ArrayList<Number>();
numbers.add(100);
numbers.get(0);

No matter call add() Method or call get() Method , Can be compiled normally , Now modify the above code , Think about whether you can compile normally .

// Kotlin
val numbers: MutableList<in Number> = ArrayList<Number>()
numbers.add(100)
val item: Int = numbers.get(0)

// Java
List<? super Number> numbers = new ArrayList<Number>();
numbers.add(100);
int item = numbers.get(0);

call get() Method will fail to compile , because numbers.get(0) The value obtained is Object The type of , So it cannot be assigned directly to int type , Contravariant is the same as covariant , Relaxed constraints on data types , But at the cost of Cannot read elements by generic type , That is, add... To the set int Data of type , call get() Method does not get int Data of type .

For the content of this section , Let's briefly summarize .

keyword (Java/Kotlin) add to Read
Covariance ? extends / out
Inversion ? super / in

Kotlin and Java The difference of array covariance

Whether it's Kotlin still Java The meaning of covariance and contravariant is the same , But the wildcards are different , But they also have differences .

Java Support array covariance , The code is as follows :

Number[] numbers = new Integer[10];

however Java Array covariance in is defective , Change the above code , As shown below .

Number[] numbers = new Integer[10];
numbers[0] = 1.0;

Can compile normally , But it will crash when running .

Because at first I will Number[] Covariant transformation Integer[], Then I added... To the array Double Data of type , So the operation will crash .

and Kotlin Our solution is very straightforward , Array covariance is not supported , There will be errors when compiling , For array inversion Koltin and Java Don't support .

Application scenarios of covariance and inversion

Covariant and inverse applications need to follow PECS(Producer-Extends, Consumer-Super) principle , namely ? extends perhaps out As a producer ,? super perhaps in As a consumer . The advantages of following this principle are , You can keep your code safe at compile time , Reduce the occurrence of unknown errors .

Covariant application

  • stay Java Wildcards are used in ? extends Represents covariance
  • stay Kotlin Key words in out Represents covariance

Covariant can only read data , Can't add data , So I can only be a producer , Provide data to the outside , So it can only be used to output , It is not used to input .

stay Koltin A covariant class in , Add before parameter out After modification , This parameter is in the current class Can only be used as the return value of a function , Or modify the read-only attribute , The code is as follows .

//  Normal compilation 
interface ProduceExtends<out T> {
    val num: T          //  For read-only properties 
    fun getItem(): T    //  Return value for function 
}

//  Compile failed 
interface ProduceExtends<out T> {
    var num : T         //  For variable attributes 
    fun addItem(t: T)   //  Parameters for function  
}

When we determine that an object is only a producer , Provide data to the outside , Or as the return value of a method , We can use ? extends perhaps out.

  • With Kotlin For example , for example Iterator#next() Method , Keyword used out, Returns each element in the collection

  • With Java For example , for example ArrayList#addAll() Method , Wildcards are used ? extends

Pass in the parameter Collection<? extends E> c As a producer to ArrayList Provide data .

Inverter applications

  • stay Java Use wildcards in ? super Represent contravariant
  • stay Kotlin Use keywords in in Represent contravariant

Inversion can only add data , Cannot read data by generics , So only as a consumer , Therefore, it can only be used to input , Cannot be used to output .

stay Koltin An inverse class in , Add before parameter in After modification , This parameter is in the current class Can only be used as an argument to a function , Or modify variable attributes .

//  Normal compilation , Parameters for function 
interface ConsumerSupper<in T> {
    fun addItem(t: T)
}

//  Compile failed , Return value for function 
interface ConsumerSupper<in T> {
    fun getItem(): T
}

When we determine that an object is only a consumer , When passed in as a parameter , Only for adding data , We use wildcards ? super Or keywords in,

  • With Kotlin For example , For example, extension methods Iterable#filterTo(), Keyword used in, Internally, it is only used to add data

  • With Java For example , for example ArrayList#forEach() Method , Wildcards are used ? super

I don't know if my friends have noticed , In the source code above , Different generic tags are used separately T and E, Actually, let's pay a little attention , There are several high-frequency generic tags in the source code TEKV wait , They are applied to different scenarios .

Marker Application scenarios
T(Type) class
E(Element) aggregate
K(Key) key
V(Value) value

This is the end of the article , Thanks for reading , It's not easy to stick to originality , Welcome to 、 give the thumbs-up 、 Share it with your friends , I will continue to share original dry goods !!!

原网站

版权声明
本文为[hi-dhl]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/186/202207050929403762.html