Java 泛型初涉

这是一篇初学者对 Java 泛型的理解,如果不对欢迎指出。

Java 中的泛型,在编译时期提供类型检查。在运行时期,为了让泛型零开销,泛型都被擦除。 擦除的方式

  1. 用 bound 替换泛型参数,如果泛型参数是 unbound 的,直接用 Object 类型替换。所以生成的字节码中,只包含原始的类,接口,和方法;
  2. 在必要的情况下生成类型的强制转换,来做到类型安全
  3. Generate bridge methods to preserve polymorphism in extended generic types. (这句没看懂)

这种类型擦除的方式保证了没有新的 class 生成,使用泛型不会增加程序的开销。

在读了很多资料之后,我发现自己对泛型的误解主要是基于从 Python 来的印象。比如在 Animals
的一个 list 中,里面即可以有 Dog
又可以有 Cat
,所以就以为在 Java 中 ArrayList<? extends Animal>
即可以有 Dog
又可以有 Cat
,认为这样声明即表示这个 ArrayList
可以放任何 Animal
的子类。其实不是的, <? extends Animal>
是一个类型声明,最终只会表示一种类型。即这个 ArrayList
即可以是一个 Dog
ArrayList
,也可以是一个 Cat
ArrayList
,但不能有 Cat
又有 Dog

基于此,Bound 其实是比较好理解的。

Upper Bound

顾名思义,就是类型的上界被确定了。

class Dog extends Animal {};
 
class Cat extends Animal {};
 
class PersianCat extends Cat {};
 
 
public class Main {
    public void upperBound() {
        ArrayList<Dog> dogs = new ArrayList<>();
 
        ArrayList<? extends Animal> dogAnimals = dogs;
        Animal animal = dogAnimals.get(0);
        System.out.println(animal);
    }
}

在这个例子中,上界是 Animal,所以 ArrayList<? extends Animal>
这个 ArrayList
就表示一种 Animal
List
。可能是 Cat,也可能是 Dog。 ArrayList<? extends Animal>
ArrayList<Dog>
的子类,所以这个赋值可以成功。

对于写,因为 ArrayList
里面可以是任何 Animal
的子类,所以无法写入。不能 .add(new Dog())
也不能 .add(new Animal())
.

对于读,读出来的都是 bound 的类型,即 Animal

Lower Bound

确定的是类型的下界,即允许这个类型的任何父类。

    public void lowerbound() {
        ArrayList<Animal> animals = new ArrayList<>();
 
        ArrayList<? super Cat> catSuper = animals;
        catSuper.add(new Cat());
        catSuper.add(new PersianCat());
        Object object = catSuper.get(0);
        System.out.println(object);
    }

下界是 Cat
,即这个 ArrayList
允许任何 Cat
的父类。所以这个 ArrayList
可以是 Animal
ArrayList
,也可以是 Cat
的,也可以是 Object
的。

对于写,是允许的,因为下界是 Cat
了,那么任何 Cat
或者 Cat
的子类,都可以被转换成 <? super Cat>
,所以允许写入 Cat
Cat
的子类。

对于读,因为不知道 ArrayList
的类型可能是什么,所以读出来的都是 Object
,即最上面的 Bound。

这段代码实际被编译器抹去泛型的字节码反编译如下:

    public void lowerbound() {
        ArrayList var1 = new ArrayList();
        var1.add(new Cat());
        var1.add(new PersianCat());
        Object var3 = var1.get(0);
        System.out.println(var3);
    }

本质上,Java 的泛型是通过编译器来实现的,编译器将我们写的有泛型的代码转换成没有泛型的代码。但是这个转换只是推导,增加类型转换,而不会生成新的类,也不会生成新的代码。在运行时不会有泛型的信息,没有额外的开销。

泛型是类型未确定,实际运行的时候,还是会确定到某一种类型上,时刻记住这是一门 静态
的语言。是否允许写入,读出来是什么类型这些问题,基于“保证”类型安全这个角度理解,就比较简单了。

更多阅读:

  1. Generics in the Java Programming Language
  2. Can’t add a ModuleInfo object to ArrayList<? extends ModuleInfo>
  3. Java generics type erasure: when and what happens?

感谢 messense
发我的资料和耐心解答。以上是个人理解,如果有误那肯定是我理解不到位。理解没有问题的话就是老师教得好。

原文 

https://www.kawabangga.com/posts/3989

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Java 泛型初涉

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址