[Copyright Statement] The source can be freely reprinted for non-commercial purposes.
Blog address: https://blog.csdn.net/ShuSheng0007/article/details/108708218
from: shusheng007
Article first published on personal blog
Article Directory
Overview
The concepts of covariance, contravariance, and resistance to variation are derived from mathematics and Java/Kotlin/C#
are mainly used in generics in programming languages . It describes the inheritance relationship between two types of collections. Interested can read this article An Illustrated Guide to Covariance and Contravariance in Kotlin . This article should belong to advanced knowledge. Generally, novice programmers have either never heard of it or heard of it but do not understand the mystery at all. Earn when you see it, this will be another step for you to advance.
definition
First, let us understand the concepts of these three nouns:
Suppose we have the following two types of collections
The first set is: Animal
and Dog
, subclasses of Dog
yesAnimal
open class Animal
class Dog : Animal()
The second set is List<Animal>
List<Dog>
List<Animal>
List<Dog>
Now here comes the question: because it Dog
is Animal
a subclass, List<Dog>
is it List<Animal>
true in Kotlin/Java?
I believe Java/Kotlin
anyone with certain programming experience can answer it, but the answer is no . The covariant, contravariant, and antivariant we are talking about here describe the relationship between the above two types of sets.
-
Covariance:
List<Dog>
YesList<Animal>
subtype -
Contravariance:
List<Animal>
yesList<Dog>
subtype -
Resistance to change (Invariant):
List<Animal>
andList<Dog>
there is no inheritance
A subtype must accept at least the same range of types as its supertype declares.
A subtype must return at most the same range of types as its supertype declares.
The situation in Java
Since Kotlin is trying to improve Java, let's first look at the situation of Java:
Resistance to change
Generics in Java are resistant to change , which means that they List<String>
are not List<Object>
subtypes. Because if it doesn't, there will be type insecurity problems.
For example, if the following code can be compiled, an exception will be thrown at runtime
List<String> strs = new ArrayList<String>();
List<Object> objs = strs;
objs.add(1);
// 尝试将Integer 转换为String,发生运行时异常 ClassCastException: Cannot cast Integer to String
String s = strs.get(0);
So the above code will report an error at compile time, which ensures type safety.
But it is worth noting that arrays in Java are covariant , so arrays will really encounter the above problems. Compilation can pass normally, but runtime exceptions will occur, so generic collections should be used in Java.
String[] strs= new String[]{"ss007"};
Object[] objs= strs;
objs[0] = 1;
Covariance
Resistant varieties would seriously restrict the flexibility of the program, for example, there is a method copyAll
, a String
content collection copy into a Object
collection, which is a matter of course.
// Java
void copyAll(Collection<Object> to, Collection<String> from) {
to.addAll(from);
}
But if Collection<E>
the addAll
method signature in is as follows, the copyAll
method will fail to compile, because through the above explanation, we know that due to the resistance to degeneration, it Collection<String>
is not Collection<Object>
a subclass, so the compilation fails.
boolean addAll(Collection<E> c);
then what should we do?
Java by wildcard parameter (wildcard type argument) to resolve, the addAll
signature can be changed as follows:
boolean addAll(Collection<? extends E> c);
? extends E
Represents the collection of subclasses that this method can receive E
or E
. This wildcard makes the generic type covariant .
Inverter
Similarly Sometimes we need to Collection<Object>
be passed to Collection<String>
it to use ? super E
, which means it can receive E
or E
parent classes, subclasses position but may receive the instance of the parent class, which makes generic types occurred inverter
void m (List<? super String){
}
The characteristics of covariance and contravariance
When using ? extends E
, you can only call the read method of the passed-in parameter but not the modification method.
When used ? super E
, you can call the input parameter modification method, but the return value type is always Object when the read method is called , which is almost useless.
Does it feel hard to understand, really hard to understand! Let's take a look at code
it together . After understanding this piece of Java, Kotlin's In
and out
keywords are at hand.
For example, there is the following interface, which has two methods, one to modify and one to read.
interface BoxJ<T> {
T getAnimal();
void putAnimal(T a);
}
Here are two ways to use wildcards, pay attention to the comments
//协变,可以接受BoxJ<Dog>类型的参数
private Animal getOutAnimalFromBox(BoxJ<? extends Animal> box) {
Animal animal = box.getAnimal();
// box.putAnimal(某个类型) 无法调用该修改方法,因为无法确定 ?究竟是一个什么类型,没办法传入
return animal;
}
//逆变,可以接受BoxJ<Animal>类型的参数
private void putAnimalInBox(BoxJ<? super Dog> box){
box.putAnimal(new Dog());
// 虽然可以调用读取方法,但返回的类型却是Object,因为我们只能确定 ?的最顶层基类是Object
Object animal= box.getAnimal();
}
Regarding how to use Java wildcards, the author of Effective Java, 3rd Edition sums it up as: PECS : stands for Producer-Extends, Consumer-Super. Combine the above code analysis and find it very insightful.
- Producer-Extends can only call the read method, provide data to the outside, and cannot call the modification method
- Consumer-Super generally only calls the modification method and consumes data obtained from the outside. It is almost useless to call the read method. The type obtained is always
Object
I suggest you try it yourself, otherwise you will still be a little confused
Are there any disadvantages in this way of Java? Kotlin official thinks that there is, but I didn't understand it very much, please forgive me. The approximate meaning is: increased complexity, but did not get the corresponding benefits.
The situation in Kotlin
Please move on to the next part... Understand Kotlin in seconds, thoroughly understand deformation annotations out and in
to sum up
Covariance, contravariance and resistance to variation, listen, listen, don’t you feel that it is a particularly advanced concept? When I first came into contact with the English document, it was called a daze, but now it seems like that. . The old saying goes: those who are difficult will not, those who meet are not difficult.
Finally, remember to like, share and favorite
The lonely village is setting sun, and the old tree jackdaw is smoky. In the shadow of a little flying hong, green hills and green waters, white grass, red leaves and yellow flowers. "Tian Jing Sha Qiu"