Understand Kotlin's Covariance, Contravariance and Invariant

[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

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: Animaland Dog, subclasses of DogyesAnimal

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 Dogis Animala subclass, List<Dog>is it List<Animal>true in Kotlin/Java?

I believe Java/Kotlinanyone 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>Yes List<Animal>subtype

  • Contravariance: List<Animal>yes List<Dog>subtype

  • Resistance to change (Invariant): List<Animal>and List<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 Stringcontent collection copy into a Objectcollection, which is a matter of course.

// Java
void copyAll(Collection<Object> to, Collection<String> from) {
     to.addAll(from);
}

But if Collection<E>the addAllmethod signature in is as follows, the copyAllmethod 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 addAllsignature can be changed as follows:

boolean addAll(Collection<? extends E> c);

? extends ERepresents the collection of subclasses that this method can receive Eor 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 Eor Eparent 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 codeit together . After understanding this piece of Java, Kotlin's Inand outkeywords 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 alwaysObject

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"

Guess you like

Origin blog.csdn.net/ShuSheng0007/article/details/108708218