java 7 中的菱形运算符允许如下代码:
List<String> list = new LinkedList<>();
但是在 Java 5/6 中,我可以简单地编写:
List<String> list = new LinkedList();
我对类型擦除的理解是这些完全相同。(无论如何,泛型在运行时都会被删除)。
为什么要为钻石烦恼?它允许哪些新功能/类型安全?如果它没有产生任何新功能,为什么他们会提到它作为一个特性?我对这个概念的理解有缺陷吗?
java 7 中的菱形运算符允许如下代码:
List<String> list = new LinkedList<>();
但是在 Java 5/6 中,我可以简单地编写:
List<String> list = new LinkedList();
我对类型擦除的理解是这些完全相同。(无论如何,泛型在运行时都会被删除)。
为什么要为钻石烦恼?它允许哪些新功能/类型安全?如果它没有产生任何新功能,为什么他们会提到它作为一个特性?我对这个概念的理解有缺陷吗?
问题与
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 语言的通用部分……它们不仅仅适用于Collection
s 的无参数构造函数!
你的理解略有缺陷。钻石运算符是一个不错的功能,因为您不必重复自己。在声明类型时定义一次类型是有意义的,但在右侧再次定义它是没有意义的。DRY 原则。
现在解释关于定义类型的所有模糊问题。您是对的,该类型在运行时被删除,但是一旦您想从具有类型定义的列表中检索某些内容,您就可以将其恢复为您在声明列表时定义的类型,否则它将丢失所有特定功能并且只有对象功能,除非您将检索到的对象转换为它的原始类型,这有时会非常棘手并导致 ClassCastException。
使用List<String> list = new LinkedList()
会给你 rawtype 警告。
此行会导致 [unchecked] 警告:
List<String> list = new LinkedList();
所以,问题转变了:为什么只有在创建新集合的情况下才会自动抑制 [unchecked] 警告?
<>
我认为,这比添加功能要困难得多。
UPD:我还认为,如果合法地使用原始类型“仅用于几件事”,那将是一团糟。
理论上,菱形运算符允许您通过保存重复的类型参数来编写更紧凑(和可读)的代码。在实践中,这只是两个令人困惑的字符,更多的是给你什么。为什么?
恕我直言,有一种清晰而简单的方法将源标记为 Java 7 比发明这些奇怪的东西更有用。在如此标记的代码中,可以在不丢失任何内容的情况下禁止原始类型。
顺便说一句,我认为不应该使用编译开关来完成。程序文件的 Java 版本是文件的一个属性,根本没有选项。使用一些微不足道的东西
package 7 com.example;
可以说清楚(您可能更喜欢更复杂的东西,包括一个或多个花哨的关键字)。它甚至可以毫无问题地编译为不同 Java 版本编写的源代码。它将允许引入新的关键字(例如,“模块”)或删除一些过时的功能(单个文件中的多个非公共非嵌套类或其他),而不会失去任何兼容性。
当您编写 时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 中发生的事情。
菱形运算符的目的只是为了在声明泛型类型时减少代码类型。它对运行时没有任何影响。
如果您在 Java 5 和 6 中指定,唯一的区别是,
List<String> list = new ArrayList();
是你必须指定@SuppressWarnings("unchecked")
的list
(否则你会得到一个未经检查的演员警告)。我的理解是,钻石运营商正试图让开发变得更容易。它与泛型的运行时执行完全无关。