Java 中菱形运算符 (<>) 的意义何在?

2022-01-30 00:00:00 java generics java-7 diamond-operator

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

The diamond operator in java 7 allows code like the following:

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

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

However in Java 5/6, I can simply write:

List<String> list = new LinkedList();

我对类型擦除的理解是这些是完全一样的.(无论如何,泛型在运行时都会被删除).

My understanding of type erasure is that these are exactly the same. (The generic gets removed at runtime anyway).

为什么要操心钻石呢?它允许哪些新功能/类型安全?如果它没有产生任何新功能,为什么他们会提到它作为一个特性?我对这个概念的理解有问题吗?

Why bother with the diamond at all? What new functionality / type safety does it allow? If it doesn't yield any new functionality why do they mention it as a feature? Is my understanding of this concept flawed?

推荐答案

问题

List<String> list = new LinkedList();

是在左侧,您使用的是 generic 类型 List<String> 在右侧您使用的是 raw 键入 LinkedList.Java 中的原始类型实际上只是为了与泛型前代码兼容而存在,并且不应该在新代码中使用,除非你绝对必须这样做.

is that on the left hand side, you are using the generic type List<String> where on the right side you are using the raw type LinkedList. Raw types in Java effectively only exist for compatibility with pre-generics code and should never be used in new code unless you absolutely have to.

现在,如果 Java 从一开始就有泛型,并且没有类型,例如 LinkedList,它们是在泛型之前创建的,那么它可能会这样做,以便构造函数如果可能,泛型类型会自动从赋值的左侧推断其类型参数.但它没有,它必须区别对待原始类型和泛型类型以实现向后兼容性.这使得他们需要制作一种略有不同但同样方便的方式来声明通用对象的新实例,而不必重复其类型参数......菱形运算符.

Now, if Java had generics from the beginning and didn't have types, such as LinkedList, that were originally created before it had generics, it probably could have made it so that the constructor for a generic type automatically infers its type parameters from the left-hand side of the assignment if possible. But it didn't, and it must treat raw types and generic types differently for backwards compatibility. That leaves them needing to make a slightly different, but equally convenient, way of declaring a new instance of a generic object without having to repeat its type parameters... the diamond operator.

List<String> 的原始示例而言list = new LinkedList(),编译器会为该赋值生成一个警告,因为它必须这样做.考虑一下:

As far as your original example of List<String> list = new LinkedList(), the compiler generates a warning for that assignment because it must. Consider this:

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 exist to provide compile-time protection against doing the wrong thing. In the above example, using the raw type means you don't get this protection and will get an error at runtime. This is why you should not use raw types.

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

然而,菱形运算符允许将赋值的右侧定义为具有与左侧相同类型参数的真正泛型实例......而无需再次键入这些参数.它允许您通过几乎与使用原始类型相同的努力来保持泛型的安全性.

The diamond operator, however, allows the right hand side of the assignment to be defined as a true generic instance with the same type parameters as the left side... without having to type those parameters again. It allows you to keep the safety of generics with almost the same effort as using the raw type.

我认为要理解的关键是原始类型(没有 <>)不能与泛型类型一样对待.当你声明一个原始类型时,你没有得到泛型的任何好处和类型检查.您还必须记住,泛型是 Java 语言的通用部分...它们不仅仅适用于 Collection 的无参数构造函数!

I think the key thing to understand is that raw types (with no <>) cannot be treated the same as generic types. When you declare a raw type, you get none of the benefits and type checking of generics. You also have to keep in mind that generics are a general purpose part of the Java language... they don't just apply to the no-arg constructors of Collections!

相关文章