美文网首页
范型--Java与kotlin

范型--Java与kotlin

作者: 紫鹰 | 来源:发表于2021-04-29 17:00 被阅读0次

    缘起(为解决什么问题而生)

    • 加强类型安全
    • 减少类型强转次数
    • 限定参数或返回值范围

    类型安全

    • 类型赋值检查
    • 类型调用检查

    协变与逆变

    Java实现

    先看一个常见的例子

        TextView tv = new Button(context);
        List<Button> buttons = new ArrayList<Button>(); 
        List<TextView> textViews = buttons;// 报错
    

    Java的范型类型会在编译过程进行类型擦除,为了保证类型安全,不允许这样赋值。

    但在实际应用中,我们经常需要这种赋值,Java中提供范型通配符? extends? super来解决这个问题

    • 上界通配符 ? extends

        List<Button> buttons = new ArrayList<Button>();
        List<? extends TextView> textViews = buttons;//合法
       
        TextView textView = textViews.get(0);// 合法
        textViews.add(textView)//报错
      

    add报错的理解:
    List<? extends TextView>类型未知,他可能是List<TextView>,也可能是List<Button>。对于List<Button>,调用add显然是不可以的,实际情况是,编译器无法确定究竟属于哪一种,因此无法执行下去,就报错了。

    • 下界通配符? super

        List<? super Button> buttons = new AyyayList<Button>()//合法
        List<? super Button> buttons = new AyyayList<TextView>()//合法
        List<? super Button> buttons = new AyyayList<Object>()//合法
        
        Object object = buttons.get(0)//get 出来的都是Object类型
        Button button = new Button(context);
        buttons.add(button)// 合法
      

    表示未知类型,所以获取出来的数据可以用Object来接,虽然不知道List中存放的类型究竟是哪种类型,但是Button一定是这些类型中的子类型,因此可以调用add

    PECS 法则:「Producer-Extends, Consumer-Super」。

    使用范型通配符? extends 来使范型支持协变,但是只能读不能修改(不能使用add方法)
    使用范型通配符? super来使范型支持逆变,但是只能写不能读(获取到的值都是Object类型)

    kotlin实现

    使用关键字 out 来支持协变,相当于Java中的 ? extends
    使用关键字 in 来支持逆变,相当于Java中的? super

    *

    Java 单独使用,相当于 ? extends Object
    kotlin中等效于 * ,相当于 out Any

    多边界

    Java中用 &amp 连接多个边界

     class Monster <T extends Animal &amp Food>{
     } 
    

    kotlin 中使用 where 关键字

     class Monster <T> where T :Animal, T :Food
    

    类型擦除

    Kotlin java为泛型声明用法执行的类型安全检测仅在编译期进行。 运行时泛型类型的实例不保留关于其类型实参的任何信息。 其类型信息称为被擦除。例如,Foo<Bar>的实例会被擦除为 Foo<*>

    • 自动类型转换
      编译阶段编译器进行类型强转

    • 多态冲突
      例父类

        class Parent<T>{
           private T valte;
           
           public T getValue(){
              return value;
           }
           
           public void setValue(T value){
              this.value = value; 
            }
            
         }
      

    子类

       class Child extends Parent<String> {
       
            @override
            public void setValue(String value){
                 super.setValue(value);
            }
            
            @overide
            public String getValue(){
                 return super.getValue();
            }
         }
    

    子类生成的字节码反编译过来,有四个方法

    setValue(String value)//重写的方法
    setValue(Object value)//编译器生成的桥方法
    String getValue()//重写的方法
    Object getValue()//编译器生成的桥方法
    

    编译器生成的桥方法的内部实现,就只是去调用我们自己重写的那两个方法

    范型实例化

    Java实现方式

    new T()无法实现,部分原因是因为擦除,而另一部分原因是因为编译器不能验证T是否具有默认构造器。

    直接传入T 对应的Class或者Type,类内部直接通过反射创建

      class. newInstance()
    

    kotlin实现方式

    在kotlin中,由于范型的强化以及阻止才出等特性的存在,使得范型实例化成为可能。
    通过 inline 和 reifiied 可以保证泛型类型被实化

    inline fun <reified T: Any> new(): T {
       val clz = T::class.java
       val mCreate = clz.getDeclaredConstructor()
       mCreate. isAccessible = true
       return mCreate. newInstance()
    }
    

    带参范型数实例化

    inline fun <reified T: Any> new(vararg params: Any): T {
       val clz = T::class.java
       val paramTypes = params.map { it::class.java }.toTypedArray()
       val mCreate = clz.getDeclaredConstructor(*paramTypes)
       mCreate. isAccessible = true
       return mCreate. newInstance(* params)
    }
    

    相关文章

      网友评论

          本文标题:范型--Java与kotlin

          本文链接:https://www.haomeiwen.com/subject/lpuwrltx.html