Comparison of Kotlin generic covariant out and contravariant in and extends and super in Java

In and out in  Kotlin are generic content. In Java, extends and super are counted as comparisons.

 And what is the difference between in and out in Kotlin and generic extends and super in java?

There is such a paragraph in the official document:

We cannot do the following simple things (this is completely safe):

// Java
void copyAll(Collection<Object> to, Collection<String> from) {
  to.addAll(from);
  // !!!对于这种简单声明的 addAll 将不能编译:
  // Collection<String> 不是 Collection<Object> 的子类型
}

(In Java, we learned this lesson the hard way, see "Effective Java" Third Edition , Article 28: Lists take precedence over arrays )

It can be seen that the official design of in and out in Kotlin is based on the lessons of java generics

In Kotlin:

in means that it can be written but not read, it is a consumer, and it is inverter;

Out can only be read but not written, is a producer, covariant;

What does this mean when I first saw this? What is the use? how to use?

 

Next, let’s take a look at generics in java

First look

List<? extends TextView> textViewsList = new ArrayList<>();

? extends TextView determines the upper bound TextView in textViewsList, but it is uncertain whether it is the TextView itself or the collection of its subclasses, so adding TextView and its subclasses will report an error, that is, it is a prohibited operation.

But TextView textView = textViewsList.get(0);//No error

That is to say, it is safe to use TextView to receive the value, so you can rest assured that the value is definitely TextView or its subclass. The summary is that values ​​can be taken, but values ​​cannot be assigned, corresponding to: out can be read but not written, producer, covariance 

Look again

List<? super TextView> textViewsList2 = new ArrayList<>();

 ? super TextView  determines that the lower bound of textViewsList2 is TextView, but it is uncertain whether it is the TextView itself or the collection of its parent class. Although it is not sure which parent class is the collection of TextView, there is no error when TextView and its subclasses are added.

But when getting the value Object object = textViewsList2.get(0);

Because it is uncertain which parent class of TextView is, the value can only be received by the top-level parent class Object.

The summary is that you can assign values ​​but cannot take values. The corresponding is: in can be written but not read, consumer, inverter

 

Out covariant correspondence? extends can only read but not modify the producer

in Inverter correspondence? super can only modify but not read consumer

This is just an analogy that is easier to understand. It is completely possible to treat in and out as brand new things and have nothing to do with java.

 

Next, let’s take a look at how to use in write only and out read only

The official documentation says so: when a class C type parameter T is declared out when it can only occur in C output position of members, but the rewards are C<Base> safe as the C<Derived> superclass.

interface MyOutClass<out T> {

    fun methodDemo1(t: T)
        //             ↑
        //此处报错 Type parameter T is declare as 'out'
        //       but occurs in 'in' position in type T

    fun methodDemo2(): T
        //             ↑
        // 放在返回值的位置就不会报错 
}

That is to say, when you decorate a generic type with out, it can only appear in the position of the return value, because it is read-only, that is, it is safe to read. If you put it in the parameter position, an error will be reported, saying that the generic type modified by out cannot be used as a parameter value. It can only read but not write

same:

interface MyInClass<in E> {

    fun methodDemo1(e: E)
    //                 ↑
    //             此处不报错

    fun methodDemo2(): E
    //                 ↑
    //此处报错 Type parameter E is declare as 'in'
    //       but occurs in 'out' position in type E
}

That is to say, when you modify a generic type with in, it can only appear in the position of the parameter value, because it only writes. If you put it in the return value position, an error will be reported, saying that the generic type modified by in cannot be used as the return value. That is, it can be written but not readable.

If you want a generic type to be used as a parameter and as a return value, then neither in nor out is needed, and it's ok without modification.

Note 1: It can only be used as a parameter or the return value is relative to the internal of this class. It is possible to put MyOutClass in a method of MyInClass as a parameter. Although the MyOutClass generic T is modified by out, it is only for MyOutClass. The class works internally.

interface MyInClass<in E> {

    fun methodDemo1(e: E)
    //                 ↑
    //             此处不报错

    fun methodDemo2(): E
    //                 ↑
    //此处报错 Type parameter E is declare as 'in'
    //       but occurs in 'out' position in type E

    fun methodOut(outClass: MyOutClass<Any>)
    //                 ↑
    // MyOutClass 在 MyInClass的方法参数位置是没问题的
}

Note 2:

class MyClass<P> {
//            ↑                      
//   这个位置是可以声明in或out  
//   可以在这里统一声明,就不必在每个对象声明处都进行声明

}
val myClass: MyClass<out TextView> = MyClass<TextView>()
//                     ↑                            ↑
//             声明处可以设置是       赋值实现是不能用in或out修饰的
//                in还是out

fun copy(from: Array<out Any>, to: Array<Any>) {
        //               ↑
        //          方法参数位置是可以的
        //  这样copy()不会做任何坏事,它无法写到from。
    }    
}

For the problem that Collection<String> cannot be added to Collection<Object> at the beginning, it is already possible in Kotlin:

interface Source<out T> {
    fun nextT(): T
}

fun demo(strs: Source<String>) {
    val objects: Source<Any> = strs // 这个没问题,因为 T 是一个 out-参数
    // ……
}

================================================================================================

 

Out goes out, it can be read but cannot be written. It is analogous to extends in java. It can be read accurately but cannot be written. It can get things from out. It is the producer of the production.

"in" means that it can be written in but cannot be read. It is analogous to super in java. You can write, but you can only use Object to receive it. You have to write things to in which is a consumer who consumes things.

It will be easier to memorize it literally.

As for out covariance; in contravariance. Just remember it. The ghost knows why it is called this.

 

Kotlin official Chinese address: click here!

 

 

 

Guess you like

Origin blog.csdn.net/u011288271/article/details/107318507