Out and in in kotlin language

Time:2021-4-30

PS: read the original, you can get the source code

In kotlin language, out means covariance and in means inversion; Covariance and inversion are not unique concepts of kotlin, such as Java and C #; In order to understand the out and in of kotlin, let’s use Java generics as an example. We need to use generics because its advantage is that it can check type safety when compiling, and all casts are automatic and implicit.

1. In Java? Extensions T and? super T

1、1 ? extends T

PS: the code is written on the Android studio tool

Create a java file named birds;

public class Birds {
 private String name;
 public Birds(String name) {
 this.name = name;
 }
 public void flight() {
 System. Out. Println ("I am" + name + ", belonging to birds, I can fly");
 }
}

Create a java file crow class and inherit birds;

public class Crow extends Birds {
 public Crow(String name) {
 super(name);
 }
}

Create a new generic class testbirds of java file and restrict generic t to be a subclass of birds;

public class TestBirds<T extends Birds> {
 public void actionBirds(List<T> birds) {
 for (T bird : birds) {
 bird.flight();
 }
 }
}

Try to pass list < crow > list as a parameter to the actionbirds method of testbirds at the program entrance;

List<Crow> list = new ArrayList<>();
 TestBirds<Birds> testBirds = new TestBirds<>();
 Crow crow = new crow ("crow");
 list.add(crow);
 
 /**
 *The red line will be compiled here
 */
 testBirds.actionBirds(list);

At this time, we pass in the list and find that the compilation fails. Let’s analyze it here: testbirds is a generic class. Before it is not used, t is uncertain. After it is used, t is determined, and it is birds; Putting list as a parameter into actionbirds method is equivalent to list < birds > Birds = list, but list < birds > Birds = list does not hold. Although crow inherits from birds, birds only stores objects of type birds, while list only stores objects of type crow. There is no relationship between birds and list.

A new actionbirds2 method is added to the testbirds class, which is modified on the basis of the actionbirds method;

 public void actionBirds2(List<? extends T> birds) {
 for (T bird : birds) {
 bird.flight();
 }
 }

At the program entrance, list < crow > list is used as a parameter to pass to actionbirds2 method of testbirds;

List<Crow> list = new ArrayList<>();
 TestBirds<Birds> testBirds = new TestBirds<>();
 Crow crow = new crow ("crow");
 list.add(crow);
 
 /**
 *The red line will be compiled here
 */
//        testBirds.actionBirds(list);
 
 testBirds.actionBirds2(list);

It’s amazing to find that the line testbirds. Actionbirds2 (list) has been compiled and passed? Here’s an analysis: pass list as a parameter into actionbirds2 method, which is equal to list <? Extend birds > Birds = list, it is established, list <? Extends birds > birds, which means that the collection stores birds and its subclass objects, which limits the previous session, while the list stores the objects of its subclass, so it is established when the code is compiled; Upper bound wildcard <? Extensions T >, declared with the extensions keyword, indicates that the parameter may be t or a subclass of T.

1、2 ? super T

On the basis of the original code above, add an actionbirds 3 method to the testbirds class;

 public void actionBirds3(List<T> birds,List<T> crows) {
 for (T crow : crows) {
 birds.add(crow);
 }
 }

Try to call actionbirds3 method of testbirds at the program entrance;

List<Crow> list = new ArrayList<>();
 TestBirds<Crow> testBirds = new TestBirds<>();
 Crow crow = new crow ("crow");
 list.add(crow);
 List<Birds> birdsList = new ArrayList<>();
 testBirds.actionBirds3(birdsList,list);

After arriving here, we found that the first parameter of actionbirds3 method reported a red compilation error. The reason is that when instantiating testbirds class object, t was replaced by Crow. Birdlist only stores data of type birds, while the first parameter of actionbirds3 method only stores data of type crow. Therefore, there is no relationship between them, so syntax compilation error.

We add a new actionbirds 4 method to testbirds, and change the first parameter on the basis of actionbirds 3;

 public void actionBirds4(List<? super T> birds,List<T> crows) {
 for (T crow : crows) {
 birds.add(crow);
 }
 }

The actionbirds4 method of testbirds is called at the program entrance with the actual parameters unchanged;

List<Crow> list = new ArrayList<>();
 TestBirds<Crow> testBirds = new TestBirds<>();
 Crow crow = new crow ("crow");
 list.add(crow);
 List<Birds> birdsList = new ArrayList<>();
 
 /**
 *Here the birds place will compile the red line
 */
//        testBirds.actionBirds3(birds,list);
 testBirds.actionBirds4(birdsList,list);

At this time, it is found that the actionbirds4 method in testbirds has been compiled and passed. Let’s analyze: the first parameter of actionbirds4 method is list <? super T> birds,? Super t is a lower bound wildcard, which indicates that the parameter type is limited to t or the parent class of T in use, and the objects of T type and t parent class are stored in bits; In the process of instantiating testbirds, crow is replaced by T. birds happens to be the parent of crow. When calling the actionbirds4 method of testbirds and passing the first parameter, it is equivalent to list <? Super crow > Birds = list, so the compilation passed.

2. Out and in in kotlin

2、1 out

on top? In the code case of extends T, the first parameter bits set of actionbirds2 method in testbirds class is added with? Extend t to limit, and then use the for loop to traverse the elements of t to get them. This operation is to read;? Extensions t defines the upper bound of the wildcard type, so we can read from it safely but not modify the data; We can refer to those objects that can only be read from as producers; List<? In order to ensure the safety of type operation, this is covariance; In kotlin, “out t” in kotlin is equivalent to “out t” in Java? extends T”; Let’s take kotlin’s out keyword as an example:

Create a new kotlin class testbirds2 and write a function with the same effect as the testbirds class actionbirds2;

class TestBirds2<T: Birds> {
 fun actionBirds2(birds: MutableList<out T>) {
 for (bird: T in birds) {
 bird.flight()
 }
 }
}

Call actionbirds2 function in testbirds2 at program entrance;

var testBirds2: TestBirds2<Birds> = TestBirds2<Birds>()
 Var Crow: Crow = crow
 var crowList: MutableList<Crow> = mutableListOf(crow)
 testBirds2.actionBirds2(crowList)

2、2 in

on top? In the code case of super T, the first parameter bits set of actionbirds4 method in testbirds class is added with? Super t is used to restrict, and then the for loop is used to traverse the T element of the second parameter crows to get it out, and then the T element is put into the birds set;? Super t defines the lower bound of the wildcard type, so we can safely modify the data from it, that is, put the T element into the bits set; We can refer to those objects that can only be modified from them as consumers; List<? The data type obtained by a type like super T > is object, which is meaningless. It can be considered as a consumer who does not carry out production, so as to ensure the safety of type operation. This is inversion; In kotlin, “in T” in kotlin is equivalent to “in T” in Java? super T”; Let’s take the in keyword of kotlin as an example:

Write a function in the testbirds2 class that has the same effect as the actionbirds4 method in the testbirds class;

 fun actionBirds4(birds: MutableList<in T>,crow: MutableList<T>) {
 for (t: T in crow) {
 birds.add(t) 
 }
 }

Call actionbirds4 function in testbirds2 at the program entrance;

var testBirds2: TestBirds2<Crow> = TestBirds2<Crow>()
 Var Crow: Crow = crow
 var crowList: MutableList<Crow> = mutableListOf(crow)
 var birdsList: MutableList<Birds> = mutableListOf()
 testBirds2.actionBirds4(birdsList,crowList)

2. Type 3 projection

The above examples of out and in are still limited in use, because t inherits from birds; Let’s talk about type projection. Before talking about type projection, let’s talk about any. Any is the ancestor class of kotlin language, similar to object in Java, but not equal to object, because any has only three functions: equals, hashcode and toString; If a class is declared as a generic class, the generic type can appear in the out position or in position, then we can declare it as covariant or inverse at the place of use, which is equivalent to projecting the type to a certain side for use, which belongs to type projection; Take the generic class mutablelist < T > for example, when you really want to instantiate mutablelist, you can add more in or out to the position of T, and project this type onto one side for use, that is, mutablelist’s read data method get or write data method add; Let’s take mutablelist as an example

Var mutableList: MutableList<out Any> = mutableListOf ("official account 2 play programming", 2,3,4,5)
 var size: Int = mutableList.size - 1
 var any: Any? = null
 for (i: Int in 0 .. size) {
 any = mutableList.get(i)
 Println ("the" + (I + 1) + "any is --" + any)
 }
 var mutableList2: MutableList<in String> = mutableListOf()
 MutableList2.add ("official account 2")
 
 /**
 *Here is the int type, and compilation will report an error, because mutablelist2 restricts in string
 */
 mutableList2.add(2)

This article ends here. Due to the limited technical level, it is inevitable that there will be mistakes in the article. You are welcome to criticize and correct.