在Java中菱形操作符(<>)的意义是什么?

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

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

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

List<String> list = new LinkedList();

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

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

167597 次浏览

这一行导致[unchecked]警告:

List<String> list = new LinkedList();

因此,问题转换为:为什么[unchecked]警告仅在创建新集合时才自动抑制?

我认为,这比添加<>特性要困难得多。

乌利希期刊指南:我还认为,如果合法地使用原始类型“只是为了一些事情”,将会出现混乱。

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

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

使用List<String> list = new LinkedList()将得到原始类型警告。

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

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

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

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

List<String> list = new ArrayList();

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

问题在于

List<String> list = new LinkedList();

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

现在,如果Java从一开始就有泛型,而没有在它有泛型之前创建的类型,比如LinkedList,它可能会使泛型类型的构造函数在可能的情况下自动从赋值的左边推断其类型参数。但它并没有这样做,为了向后兼容,它必须区别对待原始类型和泛型类型。这使得他们需要创建一个稍微不同的,但同样方便的方式来声明一个泛型对象的新实例,而不必重复其类型参数…钻石经营者。

对于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语言的一个通用部分…它们不仅仅适用于__abc1的无参数构造函数!

理论上,菱形操作符可以通过保存重复的类型参数来编写更紧凑(且可读)的代码。实际上,这只是两个令人困惑的字符,什么都没有。为什么?

  1. 理智的程序员不会在新代码中使用原始类型。因此,编译器可以简单地假设不写类型参数就可以推断它们。
  2. 菱形操作符不提供类型信息,它只是告诉编译器,“它会没事的”。所以省略它也无妨。在菱形操作符合法的任何地方,编译器都可以“推断”它。

在我看来,有一种清晰而简单的方法将源代码标记为Java 7将比发明这些奇怪的东西更有用。在这样标记的代码中,原始类型可以被禁止而不会损失任何东西。

顺便说一句。但是,我认为不应该使用编译开关。程序文件的Java版本是文件的一个属性,根本没有选项。使用像这样微不足道的东西

package 7 com.example;

你可能更喜欢一些更复杂的东西,包括一个或多个花哨的关键字。它甚至允许将为不同Java版本编写的源代码一起编译,而不会出现任何问题。它将允许引入新的关键字(例如,“module”)或删除一些过时的特性(单个文件中多个非公共非嵌套类或其他)而不失去任何兼容性。

其他回答中的所有回答都是有效的,但用例并不完全有效。如果你检查了番石榴,特别是与集合相关的东西,静态方法也做了同样的事情。例如: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");

番石榴还有其他非常强大的功能,实际上我想不出>有什么用处。

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