Java 7中的菱形运算符(<>)有什么意义?

时间:2010-11-12 16:43:24

标签: java generics java-7 diamond-operator

java 7中的菱形运算符允许使用以下代码:

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

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

List<String> list = new LinkedList();

我对类型擦除的理解是这些完全相同。 (无论如何,通用都会在运行时删除)。

为什么要钻石头呢?它允许哪些新功能/类型安全?如果它没有产生任何新功能,为什么他们将其作为功能提及?我对这个概念的理解是否有缺陷?

7 个答案:

答案 0 :(得分:478)

的问题
List<String> list = new LinkedList();

就是在左侧,您使用的是泛型类型List<String>,在右侧,您使用的是 raw 类型{{1 }}。 Java中的原始类型实际上只存在与前泛型代码的兼容性,并且永远不应该在新代码中使用,除非 你绝对必须。

现在,如果Java从一开始就具有泛型,并且没有类型(例如LinkedList),它们最初是在具有泛型之前创建的,那么它可能已经使它成为泛型类型的构造函数如果可能,自动从作业的左侧推断其类型参数。但事实并非如此,它必须以不同的方式处理原始类型和泛型类型以实现向后兼容性。这使得他们需要制作一个略有不同的,但同样方便的是,声明一个通用对象的新实例而不必重复其类型参数...菱形运算符。

就原始的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语言的通用部分 ...它们不仅适用于<>的无参数构造函数!

答案 1 :(得分:34)

您的理解存在轻微缺陷。钻石操作员是一个很好的功能,因为你不必重复自己。在声明类型时定义类型一次是有意义的,但在右侧再次定义它是没有意义的。干旱原则。

现在解释有关定义类型的所有模糊。你是正确的,在运行时删除了类型,但是一旦你想要从具有类型定义的List中检索某些内容,你就会将它作为你在声明列表时定义的类型返回,否则它会丢失所有特定的功能并且只有对象功能,除非您将检索到的对象强制转换为原始类型,这有时会非常棘手并导致ClassCastException。

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

答案 2 :(得分:16)

此行会导致[未选中]警告:

List<String> list = new LinkedList();

因此,问题转变:为什么[unchecked]警告不会仅在创建新集合的情况下自动被抑制?

我认为,添加<>功能会更加困难。

UPD :我认为如果合法使用原始类型只是为了一些事情,那将会是一团糟。

答案 3 :(得分:13)

理论上,菱形运算符允许您通过保存重复的类型参数来编写更紧凑(和可读)的代码。在实践中,它只是两个令人困惑的角色更多地给你什么。为什么呢?

  1. 没有理智的程序员在新代码中使用原始类型。所以编译器可以简单地假设通过编写没有类型参数来推断它们。
  2. 菱形运算符不提供类型信息,它只是说编译器,“它会没事”。因此,通过省略它,你不会有任何伤害。在钻石操作符合法的任何地方,编译器都可以“推断”它。
  3. 恕我直言,有一种清晰简单的方法将源标记为Java 7比发明这些奇怪的东西更有用。在如此标记的代码中,可以禁止原始类型而不会丢失任何内容。

    顺便说一下,我认为不应该使用编译开关来完成。程序文件的Java版本是文件的属性,根本没有选项。使用像

    这样简单的东西
    package 7 com.example;
    

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

答案 4 :(得分:7)

编写List<String> list = new LinkedList();时,编译器会生成“未选中”警告。您可以忽略它,但如果您曾经忽略这些警告,您可能还会错过警告,通知您有关真正的类型安全问题。

因此,编写一个不会产生额外警告的代码会更好,而菱形运算符允许您以方便的方式执行此操作而不必重复。

答案 5 :(得分:4)

所有在其他回复中说的都是有效但用例并不完全有效恕我直言。如果检出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还有其他非常强大的功能,我实际上无法想到&lt;&gt;的多少用途。

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

答案 6 :(得分:3)

钻石操作员的观点仅仅是在声明泛型类型时减少代码类型。它对运行时没有任何影响。

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

List<String> list = new ArrayList();

您必须将@SuppressWarnings("unchecked")指定给list(否则您将获得未经检查的投射警告)。我的理解是钻石操作员正在努力使开发更容易。根本没有关于泛型的运行时执行的任何事情。