Java 10:Java 7的菱形推断是否可以与本地类型推断一起工作?

40
JEP 286中,我们可以看到在JDK 10 (18.3)中将能够使用局部类型推断(var)。该JEP说明以下内容是可以编译的,这是预期的:
var list = new ArrayList<String>();  // infers ArrayList<String>

我很想知道如果我们尝试以下操作会发生什么:

var list = new ArrayList<>();

我在第二个代码片段中提出的内容是否能够编译通过?如果可以(我觉得可能不行),ArrayList 是否能够接受 Object 作为其泛型类型?

我想亲自试一试,但是我没有任何可以安装早期版本的机器。

谢谢!


3
好的,brian 的邮件列表 FAQ 部分中有一个问题是这样的。 - Naman
3个回答

33

是的,var和钻石操作符可以结合使用。编译器会推断出最具体的泛型类型:

var list = new ArrayList<>(); // Infers ArrayList<Object>
var list = new ArrayList<>(List.of(1, 2, 3)); // Infers ArrayList<Integer>

你甚至可以将它们与匿名类结合使用:

var list = new ArrayList<>() {};

23
"与之合作"是一个含糊的问题,你可能会得到模糊的答案。
类型推断并不是心灵读取,它只是约束求解。缺少类型约束,你就越有可能遇到失败或令人惊讶的结果(推断出你没想到的类型,比如Object)。
Diamond 表示:我需要的类型可能已经存在于左侧,为什么要在右侧重复呢。
局部变量类型推断表示:我需要的类型可能已经存在于右侧,为什么要在左侧重复呢。
泛型方法调用表示:我需要的类型可能已经存在于参数中,为什么要重复作为证人呢。
如果程序中已有足够的类型信息,既没有显式的构造函数类型参数,也没有左侧的目标类型,那么一切都会很好。例如:
List<String> anotherList = ...
var list = new ArrayList<>(anotherList);

在这里,编译器能够通过查看构造函数参数的类型(接受Collection<? extends E>的构造函数)来推断出ArrayList的类型参数。因此,它会在右侧推断出T=String,然后在左侧推断出ArrayList<String>
换句话说,编译器会尽可能根据给定信息做出判断。您提供的信息越少,它就越有可能失败或无法满足您的要求。
话虽如此,我认为您问错了问题。应该以“我对程序的可读性造成了多大的破坏”作为衡量标准,而不是“编译器允许我省略多少内容”。阅读代码比编写代码更重要。尽可能省略所有内容并不一定能最大化程序的可读性。您应该努力保留足够信息,以确保任何读者在面对您的程序时不会感到困惑。

13

是的,它会编译。代码中的 var

var list = new ArrayList<>();

应该推断为类型ArrayList<Object>(我相信由于擦除,人们无法精确确定元素的确切类型),这与使用以下代码相同:

ArrayList list = new ArrayList<>(); 
// without the type of the element of list specified

其中list最终被推断为ArrayList<Object>


以下是 Brian 在邮件列表的 常见问题解答 中的回答:

如果我们同时要求推理两侧会发生什么?

If you say:

var x = new ArrayList<>() 

then you're asking for the compiler to infer both the type argument to List, and the type of x.

But you've not provided enough type information for the compiler to do a good job.

In most cases, you'll get an informative compiler error telling you that you're asking for your mind to be read. In some cases, we'll fall back to inferring Object, as we currently do with:

Object o = new ArrayList<>()  // always inferred ArrayList<Object> here

2
你如何从“在大多数情况下,您将获得有信息的编译器错误”转变为“是的,它会编译”?最好的情况是,我认为Brian的描述在我们讨论的情况下存在歧义。 - Michael
2
@Michael,“是的,它会编译”是我的回答的一部分,因为我已经编译并执行了代码。Brian的陈述并不含糊,因为他在他的答案中也提到了回退到推断对象的异常情况。 - Naman
1
今天早上我一直在尝试使用var,但是我没有找到任何一个会导致编译错误的情况(在我看来,回退到Object是一个错误,但无论如何)。他的FAQ确实有歧义,因为他没有详细说明“大多数情况”和“某些情况”究竟是什么。 - Michael
1
@Michael,你真的使用了JEP286中提到的风险和假设语句吗?在那里你会看到不同的编译器错误。或者也许我对引用词的解释与你的不同。尽管如此,我认为JEP也可能需要改进并解释(详细说明)任何这样的专属用例。 - Naman
2
原始类型的 ArrayListArrayList<Object> 不同。当您尝试将该列表分配给期望泛型列表之外的某些内容时,您会立即注意到这一点,例如 var list = new ArrayList<>(); List<Number> numberList = list;ArrayList list = new ArrayList<>(); List<Number> numberList = list; - Holger
显示剩余3条评论

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