Richard Warburton 与 Raoul-Gabriel Urma 在 Devoxx UK 2015 上回顾 Java 泛型历史

  • Abraham Marín Pérez
  • 张龙

2015 年 7 月 5 日

话题:Java语言 & 开发架构

Richard 与 Raoul 向广大开发者提供关于 Java 8 的培训课程。近日,二人在 Devoxx UK 2015 上共同进行了一场演讲,回顾了 Java 泛型的起源与动机、介绍了目前一些不为人知的特性,同时对 Java 10 进行了展望。该演讲由 3 个精彩的部分构成:泛型的过去、现在与未来。

过去

正如 Richard 与 Raoul 所说,在引入泛型之前,Java 中的集合是很难使用的,因为其中包含的是 Object,我们无法确定集合中元素的具体类型。如果将任意类型的对象添加到集合中,那么其类型信息就会丢失,在获取对象时,开发者不得不手工将其强制转换为相关类型才能使用它。

这不仅会导致代码变得异常笨重,还使得我们无法限制添加到集合中的对象类型。2004 年发布的Java 5通过增加泛型特性解决了这个问题,不过为了保持向后兼容,泛型只能通过类型擦除来实现。

现在

接下来,Richard 与 Raoul 谈到了目前 Java 泛型的一些特性,特别是一些不为人知的特性:交叉类型、通配符与 F 绑定多态。

“交叉类型”特性使用起来相对简单,它指的是集合中元素的类型需要继承两个或多个类或接口。而通配符则不那么好理解。Richard 指出:

通配符是个难以理解且难以使用的特性。过去在 Java 中,你无法表达出参数的协变与逆变的概念,对于声明为某个接口类型的变量来说,你每次都需要在变量声明时表示出其类型信息。这将复杂性由库的作者推给了每一个人。

尽管如此,Richard 与 Raoul 通过将不同的通配符关联到生产者与消费者的概念上阐述了程序员如何可以从中获益。

生产者会生产出对象供其他类使用。为了让这些类知道该如何使用这些对象,我们使用了一个“契约”,表示出可以提供的最为具体的类型。该“契约”采取了继承通配符的形式,具体来说就是 Class<? extends T>。这表示泛型类所使用的类型是 T 或 T 的子类,这意味着你可以使用 T 所具有的所有行为。

与之类似,消费者接收并使用对象。为了让其他类知道可以将何种类型的对象传递给消费者,消费者需要指定保证会接收的最具体的类型。它采取了 super 通配符的形式,具体来说就是 Class<? super T>。该符号表示我们打算将类型为 T 的对象赋给消费者,这样消费者就可以处理类型 T 或 T 的父类的对象了。

最后,二位演讲者又谈到了 F 绑定多态,他们将其称作“奇特的递归泛型模式”(类比于 C++ 中奇特的递归模板模式)。在这种泛型形式中,类型参数会递归地引用泛型类型本身,如下所示:

public interface ClassName<T extends ClassName<T>>

正如演讲中所提到的,该特性在泛型类型中的方法需要返回模板类型时是非常有用的,比如说克隆对象时。

未来

在演讲的最后一部分中,二位演讲者继续介绍目前尚在讨论之中的对泛型的一些改进,这些改进可能会添加到 Java 10 中。一方面,目前正在解决原生类型的泛型问题,这样就无需自动装箱与自动拆箱了;这与项目 Valhalla紧密相关。Richard 说到,这些修改可以让程序员编写出下面这样的代码:

List<int> integers = new ArrayList<>();
integers.add(1);

Richard 说到,底层可以将其实现为 int[] 而非目前的 Integer[],这么做一方面可以减少内存占用,另一方面可以让 CPU 的缓存预先抓取器工作得更好。他补充说到:“这会改进性能,就像 C# 的具化泛型一样”。

另一方面,让库的作者在接口声明而非现在这样在接口使用时表明变化,可以简化协变与逆变的处理。这种声明变量的形式可以让大多数开发者从通配符泛型中获益,从而无需了解关于底层的更多信息,同时高级程序员还可以覆写这种默认行为以实现更加专门的代码。

查看英文原文:Richard Warburton and Raoul-Gabriel Urma Review the History of Generics in Java at Devoxx UK

Java语言 & 开发架构