范型--Java与kotlin

2021-04-29  本文已影响0人  紫鹰

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

类型安全

协变与逆变

Java实现

先看一个常见的例子

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

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

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

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

表示未知类型,所以获取出来的数据可以用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 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)
}
上一篇下一篇

猜你喜欢

热点阅读