美文网首页
Java泛型

Java泛型

作者: simonenfp | 来源:发表于2020-06-06 17:45 被阅读0次

    泛型定义

    Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型(类型实参)。

    假定我们有这样一个需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?

    答案是可以使用 Java 泛型

    使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。

    泛型原理:

    泛型是java5才引入的,虚拟机其实并不支持泛型,为了向下兼容,Java在编译期间擦除了所有的泛型信息,这样Java就不需要产生新的类型到字节码,所有的泛型类型最终都是一种原始类型,在Java运行时根本就不存在泛型信息。

    编译器如何擦除泛型:

    • 检查泛型类型,获取目标类型
    • 擦除类型变量,并替换为限定类型
      • 如果泛型类型的类型变量没有限定<T>,则用Object作为原始类型
      • 如果有限定<T extends Class1>,则用Class1作为原始类型
      • 如果有多个限定类型<T extends Class1 & Class2>,则使用第一个边界Class1作为原始类
    • 在必要时插入类型转换以保持类型安全
    • 生成桥方法以在扩展时保持多态性
    • 可以对比下类经过编译后的生成的字节码:
      从下图1可以看见泛型都没了,add和get方法的T都被Object替代了,这就是编译器擦除了泛型 图1.png
      从下图2可看到如果有限定符,被擦除后则用限定类型替代。 图2.png
      从下图3看出编译后插入了类型转换 图3.png
      如下图4,图5,根据泛型擦除我们知道,接口CageInterface的get方法编译后其实是public Object get(){};而Cage的get方法被限定为public Animal get(){},所以编译器会实现一个桥方法,内部再去调用public Animal get(){}方法,这样就保持了多态性。 图4.png 图5.png

    限定通配符(为了更灵活的转型):

    • <? extends Fruit>上界通配符(通常用于取数据):
    图6 如上图6 ,编译器报错,无法add,因为<? extends Animal>是限定animals这个引用类型范围是Animals或者其子类,只是代表这个范围,并不是具体某个引用类型,所以没法与任何类型匹配,add任何都报错(除了null),但是get是可以的,他一定是Animal的子类,只要用其父类接收就可以。
    • <? super Animal>下界通配符(通常用于接收数据):
    图7.png 如上图7,因为<? extends Animal>是限定animals引用类型范围是Animal或者它的父类(并非代表其指向的具体ArrayList对象存放的是Animal或者其父类),所以animals可以add任何Animal子类以及Animal,因为这些一定是这个引用类型的子类;也可以get,但是只能用Object接收,否则如上编译器报错,因此get没什么意义。

    这时候可以看一下Collections集合类的copy泛型方法:
    public static <T> void copy(List<? super T> dest, List<? extends T> src)
    可以看到src参数使用了上界通配符,dest参数使用了下界通配符,意味着src引用所指向的列表一定是T或者T的某个子类型,而dest引用范围是T或者它的父类,所以将src中的元素复制到dest一定是可以的,所以保证了从src复制到dest的类型正确性。

    非限定通配符:

    如List<?>,既不能读也不能写,等价于List<? extends Object>,对于List<?>编译器会进行安全检查,而List不会。

    相关文章

      网友评论

          本文标题:Java泛型

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