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参数来编写更紧凑(可读)的代码。 在实践中,这只是两个令人困惑的字符,更多的给你什么。 为什么?
- 没有理智的程序员在新代码中使用原始types。 所以编译器可以简单地假设,通过不写入types参数,你想它推断它们。
- 钻石操作员没有提供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中发生的事情。