• 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 一块使用

  • 相关阅读:
    Gym
    数学公式头文件
    除法取模(比赛常用)
    ACM-ICPC 2017 Asia Urumqi A. Coins【期望dp】
    P1494 小Z的袜子 【普通莫队】
    Codeforces Round #642 (Div. 3) E—K-periodic Garland dp
    luogu P4568 [JLOI2011]飞行路线 最短路Dijkstra+dp
    luogu P2015 二叉苹果树 树形dp
    luogu P1462 通往奥格瑞玛的道路 二分+spfa
    luogu P1879 [USACO06NOV]Corn Fields G 状态压缩dp
  • 原文地址:https://www.cnblogs.com/FCY-LearningNotes/p/14799790.html
Copyright © 2020-2023  润新知