泛型

让代码写代码

大家在写代码的时候总是会遇到迫不得已的复制与粘贴。

这很大程度上是因为代码的逻辑有重复。这个时候我们会使用到函数——一种把相同逻辑封装一下的手法。这大大降低了我们的开发难度。但是有没有一种情况,就算函数也解决不了Copy-Paste了呢?

正如我们之前的引入中所说。当相同的逻辑应用在不同的类型上时连函数也无能为力了。由于函数自带的严格得类型检查,我们不得不写出大量重复的代码。

有没有一种办法解决这个问题呢?从直观的角度来看这些代码长相十分相近,我们可以用一个代码生成器生成这一系列的代码。十分的幸运的是,现代计算机语言已经内置了这个代码生成器,并且有一些语言如C++与java还额外提供了类型检查帮助程序员。

而这个想法的起源很早甚至可以追溯到宏的诞生。在纯粹的c语言工程中可以使用宏实现类似泛型的效果。而C++则使用函数模板,这是一种真正的泛型机制,也因此诞生了.hpp文件。而在java中使用了一种被称为类型擦除的技术实现泛型。

简单的泛型示例

先来看看泛型的好处吧

private static <T extends Number> double add(T a, T b) {
    System.out.println(a + "+" + b + "=" + (a.doubleValue() + b.doubleValue()));
    return a.doubleValue() + b.doubleValue();
}

这一段代码可以等效于

那么java究竟是如何实现这一神奇的效果的呢?

类型擦除

Java泛型这个特性是从JDK 1.5才开始加入的,因此为了兼容之前的版本,Java泛型的实现采取了“伪泛型”的策略,即Java在语法上支持泛型,但是在编译阶段会进行所谓的“类型擦除”(Type Erasure),将所有的泛型表示(尖括号中的内容)都替换为具体的类型(其对应的原生态类型),就像完全没有泛型一样。理解类型擦除对于用好泛型是很有帮助的,尤其是一些看起来“疑难杂症”的问题,弄明白了类型擦除也就迎刃而解了。

泛型的类型擦除原则是:

  • 消除类型参数声明,即删除<>及其包围的部分。

  • 根据类型参数的上下界推断并替换所有的类型参数为原生态类型:如果类型参数是无限制通配符或没有上下界限定则替换为Object,如果存在上下界限定则根据子类替换原则取类型参数的最左边限定类型(即父类)。

  • 为了保证类型安全,必要时插入强制类型转换代码。

  • 自动产生“桥接方法”以保证擦除类型后的代码仍然具有泛型的“多态性”。

那么是如何进行擦除的呢?

  • 擦除类定义中的类型参数 - 无限制类型擦除

当类定义中的类型参数没有任何限制时,在类型擦除中直接被替换为Object,即形如<T><?>的类型参数都被替换为Object。

  • 擦除类定义中的类型参数 - 有限制类型擦除

当类定义中的类型参数存在限制(上下界)时,在类型擦除中替换为类型参数的上界或者下界,比如形如<T extends Number><? extends Number>的类型参数被替换为Number<? super Number>被替换为Object。

  • 擦除方法定义中的类型参数

擦除方法定义中的类型参数原则和擦除类定义中的类型参数是一样的,这里仅以擦除方法定义中的有限制类型参数为例。

java泛型的上下限

  • 先看下如下的代码,很明显是会报错的 (具体错误原因请参考后文)。

那么如何解决呢?

为了解决泛型中隐含的转换问题,Java泛型加入了类型参数的上下边界机制。<? extends A>表示该类型参数可以是A(上边界)或者A的子类类型。编译时擦除到类型A,即用A类型代替类型参数。这种方法可以解决开始遇到的问题,编译器知道类型参数的范围,如果传入的实例类型B是在这个范围内的话允许转换,这时只要一次类型转换就可以了,运行时会把对象当做A的实例看待。

  • 泛型上下限的引入

在使用泛型的时候,我们可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。

上限

下限

小结

  • 再看一个实际例子,加深印象

上述代码中的类型参数 E 的范围是<E extends Comparable<? super E>>,我们可以分步查看:

  • 要进行比较,所以 E 需要是可比较的类,因此需要 extends Comparable<…>(注意这里不要和继承的 extends 搞混了,不一样)

  • Comparable< ? super E> 要对 E 进行比较,即 E 的消费者,所以需要用 super

  • 而参数 List< ? extends E> 表示要操作的数据是 E 的子类的列表,指定上限,这样容器才够大

  • 多个限制

使用&符号

最后更新于

这有帮助吗?