Java 7中的菱形运算符允许如下代码:
List<String> list = new LinkedList<>();
但是,在Java 5/6中,我可以简单地编写:
List<String> list = new LinkedList();
我对类型擦除的理解是这些完全相同。(无论如何,泛型都会在运行时删除)。
为什么要打扰钻石呢?它允许哪些新功能/类型安全?如果它没有产生任何新功能,为什么他们将其称为功能?我对这个概念的理解有缺陷吗?
Java 7中的菱形运算符允许如下代码:
List<String> list = new LinkedList<>();
但是,在Java 5/6中,我可以简单地编写:
List<String> list = new LinkedList();
我对类型擦除的理解是这些完全相同。(无论如何,泛型都会在运行时删除)。
为什么要打扰钻石呢?它允许哪些新功能/类型安全?如果它没有产生任何新功能,为什么他们将其称为功能?我对这个概念的理解有缺陷吗?
Answers:
与问题
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
s 的无参数构造函数!
-compatibility
编译器切换,而如果不提供,javac
则将禁止所有原始类型并仅严格执行通用类型?这将使我们的代码不再那么冗长。
List<String> strings = new List<>()
是可以的,但是如果您定义private List<String> my list;
,然后在实例化页面的中途下进行my_list = new List<>()
,那就太酷了!我的清单又包含什么?哦,让我四处寻找定义。突然,钻石捷径的好处就再见了。
my_list = getTheList()
什么不同?有几种更好的方法来解决此类问题:1.使用IDE,在鼠标悬停时向您显示变量的类型。2.使用更有意义的变量名,例如private List<String> strings
3.除非确实需要,否则不要拆分变量的声明和初始化。
您的理解有些瑕疵。Diamond运算符是一个不错的功能,因为您不必重复自己的操作。在声明类型时一次定义类型是有意义的,但是在右侧再次定义它是没有意义的。DRY原理。
现在来解释有关定义类型的所有模糊性。您是对的,该类型在运行时已删除,但是一旦您想从具有类型定义的列表中检索某些内容,就可以在声明该列表时将其恢复为您定义的类型,否则它将丢失所有特定功能并且仅具有对象功能,除非您将检索到的对象转换为原始类型,否则有时会非常棘手,并导致ClassCastException。
使用List<String> list = new LinkedList()
将获得原始类型警告。
List<String> list = new LinkedList()
是正确的代码。你知道这一点,我也知道。问题(据我了解)是:为什么只有Java编译器不理解该代码是相当安全的?
List<String> list = new LinkedList()
是不正确的代码。当然可以,如果这样的话会很好!如果Java从一开始就具有泛型,并且不必处理以前是非泛型的泛型类型的向后兼容性,则可能是这样,但是确实如此。
<?>
如果与传统代码接口,则可以始终使用),并且不应该使用无用的菱形运算符。
从理论上讲,菱形运算符允许您通过保存重复的类型参数来编写更紧凑(更易读)的代码。实际上,仅仅是两个混乱的字符就什么都不给您。为什么?
恕我直言,以清晰简单的方式将源标记为Java 7会比发明这种奇怪的东西有用。在这样标记的代码中,原始类型可以被禁止而不丢失任何内容。
顺便说一句,我不认为应该使用编译开关来完成它。程序文件的Java版本是文件的属性,完全没有选项。使用一些琐碎的东西
package 7 com.example;
可以使您清楚(您可能更喜欢一些复杂的内容,包括一个或多个奇特的关键字)。它甚至可以一起编译针对不同Java版本编写的源代码,而不会出现任何问题。它将允许引入新的关键字(例如“模块”)或删除一些过时的功能(在单个文件中或多个文件中存在多个非公共的非嵌套类)而不会失去任何兼容性。
new ArrayList(anotherList)
和之间的区别new ArrayList<>(anotherList)
(特别是如果将其分配给List<String>
且anotherList
是List<Integer>
)?
new @RawType List()
。这已经是有效的Java 8语法,并且类型注释允许在需要的任何地方使用它,例如@RawType List = (@RawType List) genericMethod();
。考虑到原始类型当前会创建一个编译器警告,除非@SuppressWarnings
放置了适当的警告,@RawType
这将是一个合理的替代,并且不需要更微妙的语法。
其他回复中的所有内容都是有效的,但用例不是完全有效的恕我直言。如果检查番石榴 ,尤其是与收藏有关的东西,那么静态方法也可以做到这一点。例如,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");
番石榴还具有其他非常强大的功能,而我实际上想不到<>的许多用途。
如果它们使菱形运算符的行为成为默认值,即从表达式的左侧推断类型,或者从右侧推断左侧的类型,则将更加有用。后者就是Scala中发生的情况。
Diamond运算符的目的只是在声明泛型类型时减少代码的类型。它对运行时没有任何影响。
如果您在Java 5和6中指定,则唯一的区别是
List<String> list = new ArrayList();
是,你必须指定@SuppressWarnings("unchecked")
的list
(否则你将得到一个unchecked投警告)。我的理解是,钻石运营商正在努力使开发变得更容易。与泛型的运行时执行完全无关。