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

发布于 2024-10-02 07:36:16 字数 334 浏览 5 评论 0 原文

java 7中的菱形运算符允许像下面这样的代码:

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

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

List<String> list = new LinkedList();

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

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

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

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

離殇 2024-10-09 07:36:16

问题是

List<String> list = new LinkedList();

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

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

List 的原始示例而言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 的无参数构造函数!

The issue with

List<String> list = new LinkedList();

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.

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.

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.

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!

一念一轮回 2024-10-09 07:36:16

你的理解有点问题。钻石运算符是一个很好的功能,因为您不必重复自己。在声明类型时定义一次类型是有意义的,但在右侧再次定义它是没有意义的。 DRY 原则。

现在解释有关定义类型的所有模糊之处。您是对的,该类型在运行时被删除,但是一旦您想从具有类型定义的列表中检索某些内容,您就可以将其作为声明列表时定义的类型返回,否则它将丢失所有特定功能并且仅具有对象功能,除非您将检索到的对象转换为其原始类型,这有时可能非常棘手并导致 ClassCastException。

使用List; list = new LinkedList() 会给你 rawtype 警告。

Your understanding is slightly flawed. The diamond operator is a nice feature as you don't have to repeat yourself. It makes sense to define the type once when you declare the type but just doesn't make sense to define it again on the right side. The DRY principle.

Now to explain all the fuzz about defining types. You are right that the type is removed at runtime but once you want to retrieve something out of a List with type definition you get it back as the type you've defined when declaring the list otherwise it would lose all specific features and have only the Object features except when you'd cast the retrieved object to it's original type which can sometimes be very tricky and result in a ClassCastException.

Using List<String> list = new LinkedList() will get you rawtype warnings.

独自唱情﹋歌 2024-10-09 07:36:16

此行会导致 [unchecked] 警告:

List<String> list = new LinkedList();

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

我认为,这比添加 <> 功能要困难得多。

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

This line causes the [unchecked] warning:

List<String> list = new LinkedList();

So, the question transforms: why [unchecked] warning is not suppressed automatically only for the case when new collection is created?

I think, it would be much more difficult task then adding <> feature.

UPD: I also think that there would be a mess if it were legally to use raw types 'just for a few things'.

谷夏 2024-10-09 07:36:16

理论上,菱形运算符允许您通过保存重复的类型参数来编写更紧凑(且可读)的代码。实际上,这只是两个令人困惑的字符,不会给你任何帮助。为什么?

  1. 没有理智的程序员会在新代码中使用原始类型。因此,编译器可以简单地假设,通过不编写任何类型参数,您希望它推断它们。
  2. 菱形运算符不提供类型信息,它只是告诉编译器“一切都会好的”。因此,忽略它不会造成任何伤害。在钻石运营商合法的任何地方,编译器都可以“推断”它。

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

顺便说一句,我认为不应该使用编译开关来完成。程序文件的Java版本是文件的一个属性,根本没有选项。使用尽可能简单的东西

package 7 com.example;

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

In theory, the diamond operator allows you to write more compact (and readable) code by saving repeated type arguments. In practice, it's just two confusing chars more giving you nothing. Why?

  1. No sane programmer uses raw types in new code. So the compiler could simply assume that by writing no type arguments you want it to infer them.
  2. The diamond operator provides no type information, it just says the compiler, "it'll be fine". So by omitting it you can do no harm. At any place where the diamond operator is legal it could be "inferred" by the compiler.

IMHO, having a clear and simple way to mark a source as Java 7 would be more useful than inventing such strange things. In so marked code raw types could be forbidden without losing anything.

Btw., I don't think that it should be done using a compile switch. The Java version of a program file is an attribute of the file, no option at all. Using something as trivial as

package 7 com.example;

could make it clear (you may prefer something more sophisticated including one or more fancy keywords). It would even allow to compile sources written for different Java versions together without any problems. It would allow introducing new keywords (e.g., "module") or dropping some obsolete features (multiple non-public non-nested classes in a single file or whatsoever) without losing any compatibility.

七色彩虹 2024-10-09 07:36:16

当你写 List; list = new LinkedList();,编译器会产生“unchecked”警告。您可能会忽略它,但如果您习惯于忽略这些警告,您也可能会错过通知您真正的类型安全问题的警告。

因此,最好编写不会生成额外警告的代码,并且菱形运算符允许您以方便的方式完成此操作,而无需不必要的重复。

When you write List<String> list = new LinkedList();, the compiler produces an "unchecked" warning. You may ignore it, but if you got used to ignoring these warnings, you may also miss a warning that notifies you about a real type-safety problem.

So, it's better to write code that doesn't generate extra warnings, and the diamond operator allows you to do it in a convenient way without unnecessary repetition.

╰沐子 2024-10-09 07:36:16

其他回复中的所有内容都是有效的,但恕我直言,用例并不完全有效。如果您查看 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 中发生的情况。

All said in the other responses are valid but the use cases are not completely valid IMHO. If one checks out Guava and especially the collections related stuff, the same has been done with static methods. E.g. Lists.newArrayList() which allows you to write

List<String> names = Lists.newArrayList();

or with static import

import static com.google.common.collect.Lists.*;
...
List<String> names = newArrayList();
List<String> names = newArrayList("one", "two", "three");

Guava has other very powerful features like this and I actually can't think of much uses for the <>.

It would have been more useful if they went for making the diamond operator behavior the default, that is, the type is inferenced from the left side of the expression or if the type of the left side was inferenced from the right side. The latter is what happens in Scala.

成熟的代价 2024-10-09 07:36:16

菱形运算符的目的只是为了在声明泛型类型时减少代码的输入。它对运行时没有任何影响。

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

List<String> list = new ArrayList();

是您必须将 @SuppressWarnings("unchecked") 指定到 list (否则您将得到未经检查的强制转换警告)。我的理解是钻石运营商正在努力让开发变得更容易。它与泛型的运行时执行完全无关。

The point for diamond operator is simply to reduce typing of code when declaring generic types. It doesn't have any effect on runtime whatsoever.

The only difference if you specify in Java 5 and 6,

List<String> list = new ArrayList();

is that you have to specify @SuppressWarnings("unchecked") to the list (otherwise you will get an unchecked cast warning). My understanding is that diamond operator is trying to make development easier. It's got nothing to do on runtime execution of generics at all.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文