out and in in kotlin language

In the kotlin language, out means covariance, and in means inversion; covariance and inversion are not unique concepts of kotlin, such concepts as Java and C#; in order to understand out and in in kotlin language, we Let’s take Java’s generics as an example first. We need to use generics because its advantage is that type safety can be checked at compile time, and all mandatory conversions are automatic and implicit.

1. ? extends T and ? super T in Java

1, 1? extends T

ps: the code is written on the AndroidStudio tool

Create a Java file Birds;

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

Create a Java file crow class Crow and inherit Birds;

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

Create a new generic class TestBirds in a Java file and restrict the 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();
}
}
}

At the entry point of the program, try to use the List list as a parameter to pass to the actionBirds method of TestBirds;

 List<Crow> list = new ArrayList<>();
TestBirds<Birds> testBirds = new TestBirds<>();
Crow crow = new Crow("crow");
list. add(crow);
/**
* Here the list will compile and report a red line
*/
testBirds. actionBirds(list);

At this time, the list is passed in and the compilation fails. Let’s analyze it here: TestBirds is a generic class. Before it is used, T is uncertain. After use, T is determined. It is Birds; pass the list as a parameter to actionBirds In the method, it is equivalent to List birds = list, but List birds = list is not true. Although Crow inherits from Birds, birds only save objects of type Birds, and list only saves objects of type Crow. It has nothing to do with 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();
}
}

Use the List list as a parameter to pass to the actionBirds2 method of TestBirds at the program entry;

 List<Crow> list = new ArrayList<>();
TestBirds<Birds> testBirds = new TestBirds<>();
Crow crow = new Crow("crow");
list. add(crow);
/**
* Here the list will compile and report a red line
*/
// testBirds. actionBirds(list);
testBirds. actionBirds2(list);

At this time, I found that the line of code testBirds.actionBirds2(list) was compiled and passed. Isn’t it amazing? Let’s analyze it here: Passing the list as a parameter into the actionBirds2 method is equivalent to List birds = list, which is established. List birds means that the collection stores Birds and Birds subclass objects. The last term is limited, and the list stores the objects of the Birds subclass, so the code compiles and passes, and it is established; the upper bound wildcard < ? extends T>, declared with the extends keyword, indicates that the parameter may be T or T A subclass of .

1, 2 ? super T

Based on the original code above, add an actionBirds3 method in the TestBirds class;

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

Try calling the actionBirds3 method of TestBirds at the program entry;

 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, I found that the first parameter of the actionBirds3 method reported a red compilation error. The reason was that when instantiating the TestBirds class object, T was replaced by Crow, and the birdsList only stored data of the Birds type, while the first The parameter only stores data of Crow type, so the two have nothing to do with each other, so the syntax compilation error.

We add a new actionBirds4 method in TestBirds, and change the first parameter on the basis of actionBirds3;

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

With the actual parameters unchanged, call the actionBirds4 method of TestBirds at the program entry;

 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 and report a red line
*/
// testBirds. actionBirds3(birds, list);
testBirds. actionBirds4(birdsList, list);

At this time, it was found that the actionBirds4 method in TestBirds was compiled and passed. Let’s analyze it: the first parameter of the actionBirds4 method is List birds, and ? super T is the lower limit wildcard, which means that the parameter type is limited to T or The parent class of T, birds store objects of T type and T parent class; in the process of instantiating TestBirds, replace Crow with T, Birds is just the parent class of Crow, call the actionBirds4 method of TestBirds to pass the first The parameter is equivalent to List birds = list, so the compilation is passed.

2. out and in in kotlin

2, 1 out

In the code case of ? extends T above, the first parameter of the actionBirds2 method in the TestBirds class is added ? extends T to limit the birds collection, and then use the for loop to traverse the elements of T to extract them. This operation is reading; ? extends T defines the upper bound of the wildcard type, so we can safely read from it but not modify the data; we can call those objects that can only be read from it called producers; List such The type does not consume the producer to ensure the safety of the type operation, which is covariance; it is expressed by out in kotlin, and “out T” in kotlin is equivalent to “? extends T” in Java; the out key of kotlin is used below Words for example:

Create a new kotlin class TestBirds2 and write a function with the same effect as TestBirds class actionBirds2;

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

Call the actionBirds2 function in TestBirds2 at the program entry;

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

2, 2 in

In the above code case of ? super T, the first parameter birds collection of the actionBirds4 method in the TestBirds class is limited by adding ? The T element is put into the birds collection; ? super T defines the lower bound of the wildcard type, so we can safely modify data from it, that is, put the T element into the birds collection; we can put those that can only be modified from it The object is called a consumer; the data type obtained by a type such as List is Object, which is meaningless and can be considered as a non-producing consumer to ensure the safety of the type operation. This is contravariance; in kotlin Expressed by in, “in T” in kotlin is equivalent to “? super T” in Java; the following uses 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 the actionBirds4 function in TestBirds2 at the program entry;

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

2, 3 type projection

The above examples of out and in are still limited to use because of the limitation that T inherits from Birds; here we will talk about type projection. Before talking about type projection, we will talk about Any. Any is the ancestor class of the Kotlin language, similar to Java. Object, but not equal to Object, because Any only has three functions: equals, hashCode and toString; declare a class as a generic class, the generic type can appear in the out position, or in the in position, we can Declaring it as covariant or contravariant at the point of use is equivalent to projecting this type to a certain side for use, which belongs to type projection; take the generic class MutableList as an example, when you really want to instantiate MutableList, You can add more in or out to the position of T, and project this type to a certain side for use, that is, the method get of reading data or the method add of writing data in MutableList; the following is an example of writing code in MutableList:

 var mutableList: MutableList<out Any> = mutableListOf("Official account Xiao Er plays 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("th" + (i + 1) + "any is--" + any)
}
var mutableList2: MutableList<in String> = mutableListOf()
mutableList2.add("Official account Xiaoer play programming")
/**
* Here, the Int type will report an error when compiling, because mutableList2 has an in String restriction
*/
mutableList2.add(2)

Reposted from: out and in_kotlin in out_liujun3512159’s blog in kotlin language – CSDN blog

The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. Java skill treeHomepageOverview 117445 people are studying systematically