Java 7中的钻石操作符有什么意义?

Java 7中的钻石操作符允许如下代码:

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

但是在Java 5/6中,我可以简单地写出:

 List<String> list = new LinkedList(); 

我对types擦除的理解是这些完全一样。 (无论如何,这个generics在运行时会被删除)。

为什么要钻石呢? 它允许什么新的function/types安全? 如果它不产生任何新的function,为什么他们提到它作为一个function? 我对这个概念的理解是否有缺陷?

与的问题

 List<String> list = new LinkedList(); 

在左侧,您使用的是genericstypesList<String> ,在右侧使用原始typesLinkedList 。 Java中的原始types实际上只存在与前generics代码的兼容性,不应该在新代码中使用,除非您绝对需要。

现在,如果Java从一开始就具有generics,并且没有types,例如最初在generics之前创build的LinkedList ,那么它可能会使genericstypes的构造函数自动从如果可能,分配的左侧。 但是没有,为了向后兼容,它必须以不同的方式处理原始types和generics。 这使得他们需要做一个稍微不同的 ,但同样方便的方式来声明一个通用对象的新实例,而不必重复它的types参数…钻石操作符。

就你最初的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); 

generics存在提供编译时防止做错事。 在上面的例子中,使用原始types意味着你没有得到这个保护,并会在运行时得到一个错误。 这就是为什么你不应该使用原始types。

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

但是菱形运算符允许将赋值的右侧定义为一个具有与左侧相同的types参数的真正的generics实例,而不必再次input这些参数。 它使您可以像使用原始types一样保持仿制药的安全。

我认为要理解的关键是原始types(没有<> )不能被视为一般types相同。 当你声明一个原始types时,你不会得到generics的好处和types检查。 您还必须记住, generics是Java语言的通用部分 …它们不仅适用于Collection的无参数构造方法!

你的理解有点瑕疵。 钻石操作员是一个很好的function,因为您不必重复自己。 在声明types时定义一次types是有意义的,但是在右侧再次定义它是没有意义的。 干的原则。

现在来解释一下关于定义types的所有疑问。 你是对的,types在运行时被删除,但是一旦你想从types定义的列表中取出某些东西,你可以将它作为你在声明列表时定义的types,否则它将失去所有特定的function,对象的function,除非你将检索到的对象转换为原始types,这有时会非常棘手,并导致ClassCastExceptionexception。

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

这一行导致[unchecked]警告:

 List<String> list = new LinkedList(); 

所以,这个问题转变:为什么[unchecked]警告不会被自动抑制,只是在创build新集合的情况下?

我认为,join<>function会比较困难。

UPD :我也认为如果合法地使用原始types只是为了一些东西,那将会是一团糟。

理论上,钻石操作符允许您通过保存重复的types参数来编写更紧凑(可读)的代码。 在实践中,这只是两个令人困惑的字符,更多的给你什么。 为什么?

  1. 没有理智的程序员在新代码中使用原始types。 所以编译器可以简单地假设,通过不写入types参数,你想它推断它们。
  2. 钻石操作员没有提供types信息,只是说编译器,“它会没事的”。 所以通过省略它,你不会有任何伤害。 在钻石经营者合法的任何地方,都可以由编制者“推测”。

恕我直言,有一个清晰和简单的方式来标记源为Java 7将比发明这样的奇怪的事情更有用。 在如此标记的代码中,原始types可以被禁止而不会丢失任何东西。

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

 package 7 com.example; 

可以说清楚(你可能更喜欢一些更复杂的东西,包括一个或多个奇特的关键字)。 它甚至可以编写针对不同Java版本编写的源代码,没有任何问题。 这将允许引入新的关键字(例如,“模块”)或删除一些过时的function(多个非公共的非嵌套类在单个文件或任何其他),而不失去任何兼容性。

当你写List<String> list = new LinkedList(); ,编译器会产生一个“未经检查”的警告。 您可以忽略它,但如果您曾经忽略这些警告,则可能会错过一个警告,通知您实际的types安全问题。

因此,最好编写一个不会产生额外警告的代码,而钻石操作员可以方便地执行此操作,而不会造成不必要的重复。

菱形运算符的要点是在声明generics时简化代码的键入。 它对运行时没有任何影响。

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

 List<String> list = new ArrayList(); 

是你必须指定@SuppressWarnings("unchecked")list (否则你会得到一个未经检查的转换警告)。 我的理解是钻石经营者正在努力使开发更容易。 在generics的运行时执行上没有任何事情可做。

所有在其他答复中说的是有效的,但用例不是完全有效的恕我直言。 如果检查出番石榴 ,尤其是collections相关的东西,静态方法也一样。 例如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"); 

番石榴有这样的其他非常强大的function,我实际上不能想到太多的用途<>。

如果他们将钻石算子的行为设为默认值,那么这会更有用,也就是说,types是从expression式的左边推导出来的,或者左边的types是从右边推导出来的。 后者是在Scala中发生的事情。

Interesting Posts