数组初始化器需要明确的目标类型 - 为什么?

27

根据JEP 286:本地变量类型推断的描述

我想知道引入这样一个限制的原因是什么,即:

Main.java:199: error: cannot infer type for local variable k

    var k = { 1 , 2 };
        ^   
(array initializer needs an explicit target-type)
所以对我来说,逻辑上应该是:

var k = {1, 2}; // Infers int[]
var l = {1, 2L, 3}; // Infers long[]

因为Java编译器已经能够正确地推断出数组的类型:

void decide() {
    arr(1, 2, 3);  // call  void arr(int ...arr)
    arr(1, 2L, 3); // call  void arr(long ...arr)
}

void arr(int ...arr) {
}

void arr(long ...arr) {
}

那么,障碍是什么?

2个回答

33
每当我们提高Java类型推断的范围时,就会出现一大堆“但是你也可以推断这个,为什么不这样做?”(或者有时候更不客气)。以下是关于设计类型推断方案的一些通用观察:
- 推断方案总会有局限性;始终存在边际情况无法推断答案,或者推断出令人惊讶的结果。我们越努力推断所有东西,就越可能推断出令人惊讶的事情。这并非总是最佳权衡。 - 很容易挑选“但你肯定可以在这种情况下推断”的例子。但如果这些情况与其他没有明显答案的情况非常相似,则只是把问题转移了——“为什么X可以但Y不行,而X和Y都是Z?” - 推断方案始终可以处理增量情况,但几乎总会产生副作用,例如在其他情况下获得更差的结果、增加不稳定性(看起来毫不相关的更改可能会改变推断类型)或增加复杂性。你不应该仅仅优化可以推断的情况数量;你还应该优化受过教育的用户能够预测哪些情况可以起作用,哪些情况不能起作用。在这里绘制简单的界线(例如,不要费劲地推断数组初始化程序的类型)通常是一个胜利之举。 - 鉴于总会存在局限性,选择一个更小但定义更清晰的目标通常更好,因为这简化了用户模型。(参见关于“为什么我不能使用私有方法的返回类型进行类型推断”的相关问题。答案是我们本来可以这样做,但结果是为了微不足道的表现而产生更复杂的用户模型。我们称之为“复杂度回报率低”)。

虽然我并不完全相信,但我已经很满足能够从这个JEP的作者那里得到一个答案了 :-D 谢谢 - Andremoniy

23

来自邮件列表platform-jep-discuss的信息,Brian Goetz在 Reader Mail Bag for Thursday (Thu Mar 10 15:07:54 UTC 2016)中提到:

  1. 为什么在初始化为数组时不能使用var,例如:

    var ints = { 1, 2, 3 }
    

规则是:我们通过将初始化程序视为独立表达式并导出其类型来推导变量的类型。但是,数组初始化程序(如Lambda和方法引用)是多态表达式 - 它们需要目标类型才能计算其类型。因此,它们被拒绝了。

我们可以让它工作吗?可能可以。但这会给该特性增加很多复杂性,而受益者大多只是个别情况。我们希望这是一个简单的特性。

简写的数组初始化程序从声明中获取其类型信息,但由于此处声明使用的是var,因此必须明确指定。

你需要在以下选项之间进行选择:

var k = new int[]{ 1 , 2 };
或者
int[] k = { 1 , 2 };

允许var k = { 1 , 2 }会改变已经存在的语法糖的语义。在int[] n = { 1, 2 }的情况下,类型由声明确定。如果允许var n = { 1, 2 },则类型突然由初始化程序本身确定。这可能会导致(更容易创建)编译器错误或不明确性。


5
假设 var n = 1; 起作用并假定为 int 类型?如果是这样,为什么 var a = {1, 2}; 不会假定为 int[] 类型呢?作为程序员,如果需要的话,我肯定可以指定其他类型。 - T.J. Crowder
3
在Java 9及之前的版本中,可以使用double[] a = { 1, 2 }这样的方式定义数组类型,因此数组类型并不是从数字字面量中推断出来的,而且在var中仍然保留了这一规则。但是,如果var a = 1 推断为整型,那么可以推断var a = { 1, 2 }int[]。但除非var的设计者之一在此处添加评论,否则我认为他们选择了保守的方式。 - Mark Rotteveel
3
因为你实际上要求我们读取Java语言设计者的想法(或者深入讨论线程),以此推断出答案。 - Mark Rotteveel
3
无论如何,我找到了Brian Goetz的相关引用,并从我的答案中删除了一些个人观点。 - Mark Rotteveel
4
请注意,在左侧使用清单类型或在右侧使用 new 表达式的选择方式与结合 var 和“钻石”类似,因为有几种方式可以提供所需的类型信息,并且您可以选择其中一种。(这不是完美的比较,因为有时您可以同时使用 var 和钻石,但原则是相同的:以某种形式提供足够的类型信息)。 - Brian Goetz
显示剩余13条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接