java 7中的diamond操作符允许如下代码:

List<String> list = new LinkedList<>();

然而在Java 5/6中,我可以简单地写:

List<String> list = new LinkedList();

我对类型擦除的理解是它们是完全相同的。(无论如何泛型都会在运行时被删除)。

为什么要为钻石费心呢?它允许哪些新的功能/类型安全?如果它没有产生任何新的功能,他们为什么要把它作为一个特性来提及呢?我对这个概念的理解有缺陷吗?


当前回答

其他回答中的所有回答都是有效的,但用例并不完全有效。如果你检查了Guava,特别是与集合相关的东西,静态方法也做了同样的事情。例如Lists.newArrayList(),它允许你写入

List<String> names = Lists.newArrayList();

或者使用静态导入

import static com.google.common.collect.Lists.*;
...
List<String> names = newArrayList();
List<String> names = newArrayList("one", "two", "three");

Guava还有其他非常强大的特性,实际上我想不出<>有什么用处。

如果他们将菱形操作符行为作为默认值,也就是说,从表达式的左侧推断类型,或者从右侧推断左侧的类型,这将会更有用。后者是在Scala中发生的事情。

其他回答

其他回答中的所有回答都是有效的,但用例并不完全有效。如果你检查了Guava,特别是与集合相关的东西,静态方法也做了同样的事情。例如Lists.newArrayList(),它允许你写入

List<String> names = Lists.newArrayList();

或者使用静态导入

import static com.google.common.collect.Lists.*;
...
List<String> names = newArrayList();
List<String> names = newArrayList("one", "two", "three");

Guava还有其他非常强大的特性,实际上我想不出<>有什么用处。

如果他们将菱形操作符行为作为默认值,也就是说,从表达式的左侧推断类型,或者从右侧推断左侧的类型,这将会更有用。后者是在Scala中发生的事情。

你的理解有点瑕疵。菱形操作符是一个很好的特性,因为你不需要重复自己。在声明类型时定义一次类型是有意义的,但在右侧再次定义它就没有意义了。DRY原则。

现在来解释定义类型的所有细节。你是对的,类型在运行时被删除,但一旦你想从一个带有类型定义的List中检索一些东西,你就会把它作为你在声明列表时定义的类型返回,否则它将失去所有特定的功能,只有Object功能,除非你将检索到的对象转换为它的原始类型,这有时会非常棘手,并导致ClassCastException。

使用List<String> List = new LinkedList()将得到rawtype警告。

菱形操作符的作用仅仅是在声明泛型类型时减少代码的类型。它对运行时没有任何影响。

如果在Java 5和Java 6中指定,唯一的区别是,

List<String> list = new ArrayList();

你必须指定@SuppressWarnings("unchecked")到列表中(否则你将得到一个未检查的强制转换警告)。我的理解是,diamond operator正试图让开发变得更容易。它与泛型的运行时执行完全没有关系。

问题在于

List<String> list = new LinkedList();

在左边,你正在使用泛型类型List<String>,而在右边,你正在使用原始类型LinkedList。原始类型在Java中实际上只是为了与预泛型代码兼容而存在的,并且不应该在新代码中使用,除非 你必须这么做。

Now, if Java had generics from the beginning and didn't have types, such as LinkedList, that were originally created before it had generics, it probably could have made it so that the constructor for a generic type automatically infers its type parameters from the left-hand side of the assignment if possible. But it didn't, and it must treat raw types and generic types differently for backwards compatibility. That leaves them needing to make a slightly different, but equally convenient, way of declaring a new instance of a generic object without having to repeat its type parameters... the diamond operator.

对于List<String> List = new LinkedList()的原始示例,编译器会为该赋值生成警告,因为它必须这样做。考虑一下:

List<String> strings = ... // some list that contains some strings

// Totally legal since you used the raw type and lost all type checking!
List<Integer> integers = new LinkedList(strings);

泛型的存在是为了提供编译时保护,防止做错误的事情。在上面的例子中,使用原始类型意味着您得不到这种保护,并将在运行时得到一个错误。这就是为什么不应该使用原始类型。

// Not legal since the right side is actually generic!
List<Integer> integers = new LinkedList<>(strings);

然而,菱形操作符允许将赋值的右侧定义为一个真正的泛型实例,具有与左侧相同的类型参数……而不需要再次输入这些参数。它允许您以几乎与使用原始类型相同的努力来保持泛型的安全性。

我认为需要理解的关键是原始类型(没有<>)不能与泛型类型同等对待。当您声明原始类型时,您无法获得泛型的任何好处和类型检查。您还必须记住,泛型是Java语言的通用部分……它们不仅仅适用于集合的无参数构造函数!

当你写List<String> List = new LinkedList();,编译器产生一个“unchecked”警告。您可以忽略它,但是如果您习惯了忽略这些警告,那么您也可能会错过通知您实际类型安全问题的警告。

所以,最好写一段不产生额外警告的代码,菱形运算符可以让你以一种方便的方式做到这一点,而没有不必要的重复。