kotlin in 与 out

kotlin 的in 与 out

kotlin: out in

java: ? extends ? super

public class Fanxing {

    private static class A {

    }

    private static class B extends A {

    }

    private static void show(List<A> o) {
        for (A a : o) {
            System.out.println(a);
        }
    }

    public static void main(String[] args) {
        List<A> aList = new ArrayList<>();
        List<B> bList = new ArrayList<>();
        show(aList);
        // 注意 下方 这个语句 会 报错  因为 Java 属于伪泛型 编译的时候会做类型擦除
        // 为了安全起见 默认禁止 协变  与 逆变 
        show(bList);
    }
}
  
-----------
show(Ljava/util/List;)V{
 0 aload_0
 1 invokeinterface #2 <java/util/List.iterator> count 1
 6 astore_1
 7 aload_1
 8 invokeinterface #3 <java/util/Iterator.hasNext> count 1
13 ifeq 36 (+23)
16 aload_1
17 invokeinterface #4 <java/util/Iterator.next> count 1
22 checkcast #5 <Fanxing$A>
25 astore_2
26 getstatic #6 <java/lang/System.out>
29 aload_2
30 invokevirtual #7 <java/io/PrintStream.println>
33 goto 7 (-26)
36 return
}
我们看一下java的字节码会发现show()方法接收的参数为List类型并且注意字节码的第22行会尝试截断操作,这就是因为Java采用的是伪泛型,编译的时候会将泛型擦除,因此java默认不允许协变与逆变。(其实协变还好 但是 逆变 肯定 抛异常 程序 闪退)

我们将show方法改变如下

  private static void show(List<? extends A> o) {
        for (A a : o) {
            System.out.println(a);
        }
    }

那么上面的代码就正常了。
但是 当我们 在show()方法中调用o.add(new A()); 就会报错,这也是为了安全。因为这是一个A子类的集合具体存储的值类型我们使用的时候无法判断,如果你向往里边添加元素,,那么大概率会发生类型截断异常,因为我们不知道数组的确切类型而且如果添加A也会造成父类强转为子类而抛出异常.
这就是协变 :Covariant

与? extends 相对的是 ?super这也是可以让 接受变量的范围扩大 但是 扩大的方向 恰好相反。

 private static void show(List<B> o) {
        for (B b : o) {
            System.out.println(b);
        }

    }

    public static void main(String[] args) {
        List<A> aList = new ArrayList<>();
        List<B> bList = new ArrayList<>();
        // 发生错误的语句
        show(aList);
        show(bList);

    }

现在 上面的程序 依然报错 但是 错误的原因 却改变了.

我们 要将show()方法改为show(List<? super B> o)这样就可以了。
逆变:Contravariant
但是 这也有个限制 就是 我们 在 show方法里边 不可以 调用show()方法带有泛型返回值的方法比如get()方法.
这也很好理解,因为 我们 在 show()方法里边不知道这个List的内容,我们调用它的get()方法但是却不知道他是什么类型这样使用非常不安全。

回到Kotlin 当中的in 与out
他们与java中的?extends 以及 ?super相对应
out: 只能被当做泛型类的输出类型:只能被使用(输出了才可以被使用)
in:只能当做泛型类的输入类型:只能被修改(输入给泛型类,让泛型类就该自身数据)

java中变量声明可以使用<?> 这就相当于<? extends object> 而kotlin泛型之中的<*>相当于

open class A<T:Any>  泛型 类型声明之中的上界  区别于 泛型声明

open class A<out T : Number> {
    fun get() {

    }
}
fun main() {
    val a: A<*> = A<Int>()
//    相当于  不等于 val aa:A<out Object> = A<Int>()
    val aa:A<out Number> = A<Int>()
}

java的多重上界

class A extends B & c{}

而在kotlin之中多重上界的表现形式略微不同

open class A<T> where T:Number,T:AA{
    fun get() {
    }
}

kotlin泛型的特殊地方是泛型可以加上reified关键字就可以使用泛型T,比如

inline fun <reified T> show() {
        println(T::class)
}

要注意的是 reified关键字必须与inline 一块使用

posted @ 2021-05-22 20:57  沙雕货  阅读(100)  评论(0编辑  收藏  举报