JEP 101:泛型目标类型推断
概述
平稳地扩展方法类型推断的范围以提供支持
- (i) 方法上下文中的推断
- (ii) 链式调用中的推断。
目标
- 在方法上下文中添加对方法类型参数推断的支持
- 在链式调用中添加对方法类型参数推断的支持
非目标
- 全局类型推断
成功指标
通过减少方法类型推断的极端情况来提升泛型的可用性。通过减少泛型方法调用中的显式类型参数来提高代码的可读性。
动机
自 JDK 5 以来,编译器会自动推断泛型方法调用中的类型参数。类型推断的重要性不仅在于显式的类型参数显得有些笨拙和冗长,更主要的原因是许多程序员对它们并不熟悉,因此在类型参数推断无法给出正确答案的情况下,他们往往束手无策。因此,尽量减少方法类型推断失败的情况非常重要;我们认为,通过添加对以下特性的支持,可以极大地改进方法类型推断:(i) 参数位置的推断 和 (ii) 链式调用中的推断。
描述
在此,我们提出了一些对现有类型参数推断支持的改进,这将显著减少泛型方法调用中对显式类型参数的需求。
i. 参数位置中的推断 GPT
考虑以下类声明:
class List<E> {
static <Z> List<Z> nil() { ... };
static <Z> List<Z> cons(Z head, List<Z> tail) { ... };
E head() { ... }
}
泛型方法的结果(例如 List.nil()
)可能会根据赋值语句的右侧推断出来:
List<String> ls = List.nil();
编译器的类型推断机制会确定对 List.nil()
调用的类型参数确实是 String。当这种泛型方法调用的结果传递给另一个方法时,编译器应该能够推断出类型,这似乎很合理,如下所示:
List.cons(42, List.nil()); //error: expected List<Integer>, found List<Object>
不幸的是,这在 JDK 5/6/7 中是不允许的——程序员唯一可用的选择是使用显式的类型参数:
List.cons(42, List.<Integer>nil());
如果类型参数推断能够扩展到在方法调用中考虑形式参数类型(目标类型),那就太好了。
ii. 链式调用中的推断 GPT
另一个相当常见的问题是,当泛型方法调用链在一起时,如下所示:GPT
String s = List.nil().head(); //error: expected String, found Object
在上述赋值中的右手边类型在类型参数推断过程中未被使用 —— 因此,程序员唯一的选择是(再次)手动指定类型参数,例如:
String s = List.<String>nil().head();
同样,如果能通过让赋值操作的右侧类型(String)在泛型方法调用链中自动传递,从而去除显式类型参数的负担,那就很好了。
替代方案
手动指定类型参数(如今天一样)。
测试
需要验证新的推断算法是否按预期运行。需要验证新的推断算法不会以意外的方式破坏向后兼容性(或者需要确保未保持向后兼容性的情况足够少见)。
没有任何特殊的平台或硬件要求。
风险与假设
如上所述,此变更的主要风险在于,任何影响方法类型推断的变更都有可能产生向后不兼容的问题。由于本文档中描述的变更涉及 Java 语言/编译器(类型系统)的一个敏感领域,因此需要测试资源来确保所提议的变更不会以意外的方式影响向后兼容性。如果需要,可以在相对较短的时间内(即在 Project Lambda 完成之前)提供支持所描述功能的原型。
依赖
这项工作依赖于 Project Lambda JEP(Java Enhancement Proposal)——Project Lambda 需要一种称为目标类型推断的新型类型推断方式,该方式用于根据 lambda 表达式使用的上下文来推断其形式参数的类型。这部分工作的一部分(方法上下文中的推断)只是对 Project Lambda 中利用的方法的泛化。另一部分工作(链式调用中的推断)是对推断的改进,这将有助于推动 Project Lambda 在开发类似 LinQ 的功能时的应用。
影响
- 客户:代码更具可读性(减少显式的类型参数)—— 更容易使用泛型方法/构造函数/菱形运算符1。
- CCC:需要一个 CCC 来涵盖类型推断的变更。
- 兼容性:新的推断方案可能会以微妙的方式改变可编译程序的集合 —— JCK 团队测试这些变更至关重要。
- 文档:无
- JCP:无
- 本地化:影响极小:可能会添加新的错误消息。
译注
-
这里是指泛型中的
<>
↩