Java 中的菱形运算符 (<>) 有什么意义?
java 7中的菱形运算符允许像下面这样的代码:
List<String> list = new LinkedList<>();
但是在Java 5/6中,我可以简单地写:
List<String> list = new LinkedList();
我对类型擦除的理解是这些是完全相同的。 (无论如何,泛型都会在运行时被删除)。
为什么要为钻石烦恼呢?它允许哪些新功能/类型安全?如果它没有产生任何新功能,为什么他们将其作为一项功能提及?我对这个概念的理解有缺陷吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
问题是
,在左侧,您使用的是通用类型
List
,而在右侧,您使用的是原始类型em> 输入LinkedList
。 Java 中的原始类型实际上只是为了与前泛型代码兼容而存在,并且永远不应该在新代码中使用,除非你绝对必须这样做。
现在,如果 Java 从一开始就有泛型,并且没有类型(例如 LinkedList),这些类型最初是在拥有泛型之前创建的,那么它可能会创建泛型类型的构造函数如果可能的话,自动从赋值的左侧推断其类型参数。但它没有,并且它必须以不同的方式处理原始类型和泛型类型以实现向后兼容性。这使得他们需要创建一个稍微不同但同样方便的方法来声明通用对象的新实例,而不必重复其类型参数......菱形运算符。
就
List 的原始示例而言list = new LinkedList(),编译器会针对该赋值生成警告,因为它必须这样做。考虑一下:
泛型的存在是为了提供编译时保护,防止做错误的事情。在上面的示例中,使用原始类型意味着您得不到这种保护,并且会在运行时收到错误。这就是为什么你不应该使用原始类型。
然而,菱形运算符允许将赋值的右侧定义为真正的泛型实例,其类型参数与左侧相同...而无需再次键入这些参数。它允许您以与使用原始类型几乎相同的努力来保持泛型的安全性。
我认为要理解的关键是原始类型(没有
<>
)不能被视为与泛型类型相同。当您声明原始类型时,您不会获得泛型的任何好处和类型检查。您还必须记住,泛型是 Java 语言的通用部分...它们不仅仅适用于Collection
的无参数构造函数!The issue with
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 typeLinkedList
. Raw types in Java effectively only exist for compatibility with pre-generics code and should never be used in new code unlessyou 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: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.
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 ofCollection
s!你的理解有点问题。钻石运算符是一个很好的功能,因为您不必重复自己。在声明类型时定义一次类型是有意义的,但在右侧再次定义它是没有意义的。 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.此行会导致 [unchecked] 警告:
因此,问题会转变:为什么仅在创建新集合时不会自动抑制 [unchecked] 警告?
我认为,这比添加
<>
功能要困难得多。UPD:我还认为,如果“仅仅为了一些事情”合法地使用原始类型,那将会是一团糟。
This line causes the [unchecked] warning:
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'.
理论上,菱形运算符允许您通过保存重复的类型参数来编写更紧凑(且可读)的代码。实际上,这只是两个令人困惑的字符,不会给你任何帮助。为什么?
恕我直言,有一种清晰简单的方法将源代码标记为 Java 7 比发明这些奇怪的东西更有用。在如此标记的代码中,可以禁止原始类型而不会丢失任何内容。
顺便说一句,我认为不应该使用编译开关来完成。程序文件的Java版本是文件的一个属性,根本没有选项。使用尽可能简单的东西
可以让它变得清晰(您可能更喜欢更复杂的东西,包括一个或多个花哨的关键字)。它甚至可以毫无问题地编译为不同 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?
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
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.
当你写
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.
其他回复中的所有内容都是有效的,但恕我直言,用例并不完全有效。如果您查看 Guava,尤其是与集合相关的内容,也会使用同样的方法静态方法。例如 Lists.newArrayList() 允许您编写
或静态导入
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
or with static import
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.
菱形运算符的目的只是为了在声明泛型类型时减少代码的输入。它对运行时没有任何影响。
如果您在 Java 5 和 6 中指定,唯一的区别
是您必须将
@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,
is that you have to specify
@SuppressWarnings("unchecked")
to thelist
(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.