首页 文章

Java 7中的钻石运算符有什么意义?

提问于
浏览
414

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

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

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

List<String> list = new LinkedList();

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

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

7 回答

  • 15

    这个问题

    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 的无参数构造函数!

  • 7

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

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

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

  • 463

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

    List<String> list = new LinkedList();
    

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

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

    UPD:我也认为如果合法地使用原始类型'just for a few things'会有一团糟 .

  • 3

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

    • 没有理智的程序员在新代码中使用原始类型 . 所以编译器可以简单地假设通过编写没有类型参数,你希望它推断它们 .

    • 菱形运算符不提供类型信息,它只是说编译器,"it'll be fine" . 因此,通过省略它,你不会有任何伤害 . 在钻石操作符合法的任何地方,编译器都可能是"inferred" .

    恕我直言,有一个清晰简单的方法将源标记为Java 7比发明这些奇怪的东西更有用 . 在如此标记的代码中,可以禁止原始类型而不丢失任何内容 .

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

    package 7 com.example;
    

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

  • 34

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

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

  • 13

    所有在其他回复中说的都是有效的,但用例并不完全有效恕我直言 . 如果检出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中发生的事情 .

  • 4

    钻石操作符的要点只是在声明泛型类型时减少代码类型 . 它对运行时没有任何影响 .

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

    List<String> list = new ArrayList();
    

    是你必须指定 @SuppressWarnings("unchecked")list (否则你将得到一个未经检查的强制转换警告) . 我的理解是钻石操作员正在努力使开发更容易 . 根本没有关于泛型的运行时执行的任何事情 .

相关问题