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 即可以是一个 DogArrayList,也可以是一个 CatArrayList,但不能有 Cat 又有 Dog

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

Upper Bound

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

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

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

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

Lower Bound

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

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

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

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

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

本质上,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 发我的资料和耐心解答。以上是个人理解,如果有误那肯定是我理解不到位。理解没有问题的话就是老师教得好。

Leave a comment

电子邮件地址不会被公开。 必填项已用*标注