JEP 286:局部变量类型推断
概括
增强 Java 语言,将类型推断扩展到带有初始值设定项的局部变量声明。
目标
我们寻求通过减少与编写 Java 代码相关的仪式来改善开发人员的体验,同时通过允许开发人员省略通常不必要的局部变量类型的清单声明来保持 Java 对静态类型安全的承诺。例如,此功能允许声明如下:
var list = new ArrayList<String>(); // infers ArrayList<String>
var stream = list.stream(); // infers Stream<String>
这种处理仅限于带有初始化器的局部变量、增强型for
循环中的索引以及传统for
循环中声明的局部变量;它不适用于方法形式、构造函数形式、方法返回类型、字段、catch 形式或任何其他类型的变量声明。
成功标准
从数量上讲,我们希望实际代码库中很大一部分局部变量声明可以使用此功能进行转换,从而推断出适当的类型。
定性地,我们希望典型用户能够理解局部变量类型推断的限制以及这些限制的动机。 (当然,这通常是不可能实现的;我们不仅无法为所有局部变量推断出合理的类型,而且一些用户将类型推断视为一种读心术,而不是一种约束求解算法,在这种情况下,没有解释似乎是明智的。)但是我们试图以这样一种方式来划清界限,以便可以清楚地说明为什么特定的构造超出了界限——并且以这样的方式编译器诊断可以有效地将它连接起来用户代码的复杂性,而不是语言的任意限制。
动机
开发人员经常抱怨 Java 所需的样板编码程度。当地人的清单类型声明通常被认为是不必要的,甚至是碍事的;有了良好的变量命名,通常就可以清楚地知道发生了什么。
为每个变量提供清单类型的需要也无意中鼓励开发人员使用过于复杂的表达式;使用较低仪式的声明语法,将复杂的链式或嵌套表达式分解为更简单的表达式的阻碍性较小。
几乎所有其他流行的静态类型“大括号”语言,无论是在 JVM 上还是在 JVM 之外,都已经支持某种形式的局部变量类型推断:C++ (auto)、C# (var)、Scala (var/val)、Go (声明与:=
)。 Java 几乎是唯一一种不支持局部变量类型推断的流行静态类型语言;至此,这应该不再是一个有争议的功能了。
Java SE 8 中类型推断的范围显着扩大,包括嵌套和链式泛型方法调用的扩展推断以及 lambda 形式的推断。这使得构建为调用链设计的 API 变得更加容易,并且此类 API(例如 Streams)非常流行,这表明开发人员已经习惯了推断中间类型。在如下调用链中:
int maxWeight = blocks.stream()
.filter(b -> b.getColor() == BLUE)
.mapToInt(Block::getWeight)
.max();
没有人会担心(甚至没有注意到)中间类型Stream<Block>
和IntStream
以及 lambda 形式的类型b
没有明确出现在源代码中。
局部变量类型推断可以在结构不太紧密的 API 中实现类似的效果;局部变量的许多用途本质上都是链,并且同样受益于推理,例如:
var path = Paths.get(fileName);
var bytes = Files.readAllBytes(path);