kotlin语言中的out和in,协变和逆变

首先协变和逆变并不是kotlin独有的,像 Java、C#都有这样的概念;

在 kotlin 语言中,out 表示协变,in 表示逆变;为了能够理解 kotlin 语言中的 out 和 in,我们先用 Java 的泛型来举例,我们需要用泛型,是因为它的好处就是在编译的时候能够检查类型安全,并且所有的强制转换都是自动和隐式的。

1、Java 中的 ? extends T 和 ? super T

? extends T

建立几个有继承关系的类,并创建测试类

public class People{}
public class Man extends People{}
public class Test<T extends People>{
        public void run(List<T> aa){
        }
        public void run2(List<? extends T> aa){
        }
        public void run3(List<? super T> aa){
        }
}

尝试调用run方法

   List<Man> list = new ArrayList<>();
   Test<People> test = new Test<>();
   test.run(list);

这时候传入 list 发现编译不通过,我们这里分析一下:Test 是泛型类,没有使用的之前,T 是不确定的,使用之后 T 是确定的,它是 People;把 list 作为参数传入 run方法中,等同于 List<People> aa= list,但是 List<People> aa= list 是不成立的,虽然 Man 继承于 People,test只保存的是 People类型的对象,list 只保存 Man 类型的对象,test和 list 是没有任何关系的。

尝试调用run2方法

   List<Man> list = new ArrayList<>();
   Test<People> test = new Test<>();
   test.run2(list);

这时候发现 test.run2(list); 这行代码编译通过了,是不是感觉很神奇?这里分析一下:把 list 作为参数传入 run2方法相等于 List<? extends People> aa= list,它是成立的,List<? extends People> aa,表示集合存储的是 People和 People的子类对象,限定了上届,而 list 存储的是 People子类的对象,所以代码编译通过,它是成立的;上界通配符 < ? extends T>,用 extends 关键字声明,表示参数可能是T,或者是T的子类。

? super T

尝试一下面方式调用

 List<People> list2 = new ArrayList<>();
 Test<Man> test2 = new Test<>();
 test2.run(list2);
 test2.run2(list2);

 这个时候发现run和run2都报错,原因是实例化 Test 类对象的时候,T 被 Man 替换了,test2只存储 People 类型的数据,而 run和run2方法的参数只存储 Man 类型的数据,所以2者没有任何关系,所以语法编译错误。

尝试下面调用方式

   List<People> list2 = new ArrayList<>();
   Test<Man> test2 = new Test<>();
   test2.run3(list2);

这时候发现 test2.run3(list); 这行代码编译通过了。run3方法的参数为 List<? super T> aa,? super T 是下限通配符,它表示在使用中限定参数类型为 T 或者是 T 的父类,aa存储的是 T 类型和 T 父类的对象;在实例化 Test 的过程中,把 Man 替换成了 T,People 刚好是 Man 的父类,调用 Test的 run3方法传递第一个参数时相当于 List<? super Man > aa= list,所以编译通过。

2、kotlin 中的 out和in

kotlin原理和上面java类同

 open class People
 class Man : People()
 class Test<T : People> {
        fun run(aa: MutableList<T>) {}
        fun run2(aa: MutableList<out T>) {}
        fun run3(aa: MutableList<in T>) {}
 }

调用方式如下:

 val list: MutableList<Man> = mutableListOf()
 val test: Test<People> = Test<People>()
 test.run2(list)

 val list2: MutableList<People> = mutableListOf()
 val test2: Test<Man> = Test<Man>()
 test2.run3(list2)

结论:

kotlin 中的 “out T” 等同于 Java 的 “?extends T” 限定了通配符类型的上届

kotlin 中的 “in T” 等同于 Java 的 “?super T” 限定了通配符类型的下届

猜你喜欢

转载自blog.csdn.net/YDHIT/article/details/132599798