Java 7中的菱形操作符允许如下代码:
1
| List<String> list = new LinkedList<>(); |
然而,在Java 5/6中,我可以简单地写:
号
我对类型擦除的理解是它们完全相同。(无论如何,泛型都会在运行时删除)。
为什么还要麻烦钻石呢?它允许什么新的功能/类型安全?如果它没有产生任何新的功能,为什么他们把它作为一个功能来提到呢?我对这个概念的理解有缺陷吗?
- 请注意,如果使用静态工厂方法,这不是问题,因为Java对方法调用进行类型推断。
- 当你禁用警告时,它实际上是无用的…:)我喜欢
- 它已经被回答了,但它也在Java教程(页面的中间):DOCS.Oracle、COM/JavaSe/Tube/Java/Guogys/Helip;
- 关于dzone的文章不错。
- 我的观点是,它是listlist=new linkedlist<的句法糖,不管左边的类型是什么>();也就是说,保持它的通用性。
问题与
在左侧,您使用的是通用类型List,在右侧,您使用的是原始类型LinkedList。Java中的原始类型只与前泛型代码兼容,不应用于新代码中,除非你绝对得这么做。
现在,如果Java从一开始就有泛型,并且没有类型,比如EDCX1(6),它最初是在有泛型之前创建的,它可能是这样做的,以便泛型类型的构造函数在可能的情况下从赋值的左手边自动推断出它的类型参数。但事实并非如此,为了向后兼容,它必须对原始类型和泛型类型进行不同的处理。这使得它们需要做一个稍微不同但同样方便的方法来声明一个通用对象的新实例,而不必重复它的类型参数…钻石操作员。
对于您最初的List list = new LinkedList()示例,编译器会为该分配生成一个警告,因为它必须这样做。考虑一下:
1 2 3 4
| 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 ); |
。
泛型的存在是为了提供编译时保护,以防做错事。在上面的示例中,使用原始类型意味着您没有得到这种保护,并且在运行时会得到一个错误。这就是为什么不应该使用原始类型的原因。
1 2
| // Not legal since the right side is actually generic!
List<Integer> integers = new LinkedList<>(strings); |
但是,diamond运算符允许将赋值的右侧定义为一个真正的泛型实例,该实例与左侧具有相同的类型参数…不必再输入这些参数。它允许您以几乎与使用原始类型相同的努力来保持泛型的安全性。
我认为要理解的关键是原始类型(没有<>)不能被视为通用类型。当您声明一个原始类型时,您将无法获得泛型的任何好处和类型检查。你还必须记住,泛型是Java语言的通用部分。它们不仅适用于Collections的no arg构造函数!
- 在您的第二个代码块中,该行LinkedList raw = a;将导致未选中的警告。这与问题中建议的方法不同。
- @罗曼:这两种情况下,右手边都是未加工的。原始类型和泛型类型不应该混合在一起,仅此而已。
- @罗曼:我换了一个例子,更清楚地表明,即使右手边是new LinkedList,也有可能出现问题。
- 向后兼容性很好,但不能以牺牲复杂性为代价。为什么Java 7不能只引入EDCOX1×5编译器开关,而如果不存在,那么EDCOX1?6将禁止所有原始类型,只执行严格的泛型类型。这将使我们的代码不再那么冗长。
- @Rosdi:同意,在新代码中不需要原始类型。但是,我非常希望在源文件中包含Java版本号(而不是使用命令行(MIS)),请参阅我的答案。
- 我并不是完全按照这个回答,我必须说有两个原因——第一,我知道没有"通用实例"这样的东西,第二,左边的信息用在右边是不正确的。采用声明对象o=New LinkedList<>();…那里没有信息,没有任何东西可以从作业的左侧"复制"到右侧。
- 我个人不喜欢钻石的使用,除非你在同一行定义和实例化钻石。List strings = new List<>()是可以的,但是如果你定义了private List my list;,然后在你用my_list = new List<>()实例化的页面的中间,那就不酷了!我的列表又包含什么?哦,让我四处寻找定义。突然间,钻石捷径的好处就消失了。
- 所以如果它是一个无参数的构造函数,那么右边使用泛型和右边使用原始类型是一样的?
- @Rmirabelle和:my_list = getTheList()有什么不同?有几种更好的方法来处理这种问题:1。使用一个显示鼠标悬停时变量类型的IDE。2。使用更有意义的变量名,如private List strings3。除非确实需要,否则不要拆分变量的声明和初始化。
- @rmirabelle,netbeans允许您按住ctrl键并单击变量以跳转到定义(使用back按钮返回),因此,如果您使用一个好的IDE,这应该很少出现问题。
- 这个答案对Java 8仍然有效吗?当我将原始类型变量分配给泛型类型变量时,不会收到任何警告。
- @ Morfidon:是的,它仍然适用于Java 8。我很确定你应该得到警告。
- @Rosdi向后兼容性不仅适用于源代码,还适用于编译的字节码。
你的理解有点缺陷。钻石接线员是一个很好的功能,因为你不必重复你自己。在声明类型时定义一次类型是有意义的,但是在右侧再次定义类型却没有意义。干燥原理。
现在来解释一下关于定义类型的所有模糊性。在运行时删除该类型是正确的,但如果希望从具有类型定义的列表中检索某个内容,则在声明该列表时会将其恢复为所定义的类型,否则它将丢失所有特定功能,并且只有对象功能,除非将检索到的对象强制转换为其原始类型,否则它可以有时很棘手,会导致类异常。
使用List list = new LinkedList()将获得RAWTYPE警告。
- List list = new LinkedList()是正确的代码。你知道,我也知道。问题(如我所理解的):为什么只有Java编译器不理解这个代码是相当安全的?
- @罗曼:List list = new LinkedList()不是正确的代码。当然,如果是的话那就太好了!如果Java从一开始就有泛型,并且不需要处理非通用的泛型类型的向后兼容性,但它确实可以。
- @ CaldJavaJava不必处理每一行的向后兼容性。在使用泛型的任何Java源文件中,应该禁止旧的非泛型类型(如果使用到遗留代码,则总是可以使用EDCOX1 2),而不可用的菱形操作符不应该存在。
此行导致[未选中]警告:
因此,问题转换为:为什么[未选中]警告不会仅在创建新集合时自动取消?
我认为,这比添加<>特性要困难得多。
厄普:我还认为,如果法律上只为一些事情使用原始类型,那将是一团糟。
理论上,Diamond运算符允许您通过保存重复的类型参数来编写更紧凑(和可读)的代码。实际上,只不过是两个让人困惑的字符而已。为什么?
没有理智的程序员在新代码中使用原始类型。所以编译器可以简单地假设,通过不编写类型参数,您希望它推断它们。
Diamond运算符不提供类型信息,它只是说编译器"会很好的"。所以省略它,你就不会有任何伤害。在菱形运算符合法的任何地方,编译器都可以"推断"它。
IMHO,有一个清晰而简单的方法来标记一个源作为Java 7将比发明这些奇怪的东西更有用。在如此标记的代码中,可以禁止原始类型,而不会丢失任何内容。
顺便说一句,我不认为应该使用编译开关来完成。程序文件的Java版本是文件的属性,根本没有选择。使用一些像
可以说清楚(你可能更喜欢复杂的东西,包括一个或多个花哨的关键词)。它甚至可以编译为不同Java版本编写的源,而不会出现任何问题。它允许引入新的关键字(例如,"module")或删除一些过时的特性(单个文件中的多个非公共非嵌套类或其他类),而不会失去任何兼容性。
- 你考虑过new ArrayList(anotherList)和new ArrayList<>(anotherList)之间的区别吗(特别是当它被分配给List和anotherList是List时)?
- @保罗·贝洛拉:没有。让我惊讶的是,这两个都是编译的。戴钻石的人甚至没有警告。不过,我看不出这有什么意义,你能详细解释一下吗?
- 对不起,我解释得不太好。请看这两个例子的区别:ideone.com/uyhagh和ideone.com/ankg3t。我只是指出,使用diamond运算符而不是原始类型很重要,至少在传入具有泛型边界的参数时。
- 实际上,我没有花时间去读科林德的答案——他引用了几乎相同的话。
- 我在博客中做了一个评论,<和>被过滤掉,很难理解其中的含义。我想说的是,如果我写的是像List list=new ArrayList();这样的行,那么编译器就没有理智的理由认为我需要一个原始类型,一个存在于支持前泛型代码的工件,而变量的List类型清楚地表明这不是前泛型代码(并且明确地说我不希望像其他人一样使用原始类型)。我写了一封信。
- @霍尔格是的,这是有道理的。使用f(new List())可能会变得更复杂,但我想可以假设您实际上不需要行类型,除非您明确要求(例如使用List list=)。我不确定所有的边缘案件,但我和你在一起。+++尽管如此,假设需要一个特殊的语法,并且是我为一个原始类型的构造函数发明了一个语法,我还是使用new List<>()。D
- 那么,Java 8会从EDCOX1的8个参数类型推断出它的类型,它可能是通用的,而调用方可能是遗留的,但是很难想象,强制类型推断如何在这里产生问题,除非类型真的是不兼容的。另一方面,当从前通用代码迁移到通用代码而不是使用原始类型时,f(Arrays.asList(new UnmatchingType[num]))也会失败。这也是一个古老的问题,为什么泛型对待构造函数的方式与方法不同。
- 所以,如果我们要为原始类型引入一种新的语法,对于少数真正需要它的地方,为什么不使用像new @RawType List()这样的语法呢?这已经是有效的Java 8语法和类型注释允许在需要的每个地方使用它,例如EDCOX1(11)。考虑到原始类型当前会创建一个编译器警告,除非放置了适当的@SuppressWarnings,@RawType将是一个合理的替换,不需要更精细的语法。
当您编写List list = new LinkedList();时,编译器会生成一个"unchecked"警告。您可以忽略它,但是如果您曾经忽略这些警告,您也可能会错过一个通知您实际类型安全问题的警告。
因此,最好编写一个不会产生额外警告的代码,Diamond操作符允许您以方便的方式执行,而不需要重复。
其他响应中所说的都是有效的,但是用例不是完全有效的imho。如果您检查了guava,特别是与集合相关的内容,那么静态方法也会这样做。例如,lists.newarraylist(),它允许您编写
1
| List<String> names = Lists.newArrayList(); |
号
或静态导入
1 2 3 4
| import static com.google.common.collect.Lists.*;
...
List<String> names = newArrayList();
List<String> names = newArrayList("one","two","three"); |
番石榴还有其他非常强大的功能,像这样,我真的想不出它有什么用途。
如果将菱形运算符行为设为默认行为,即从表达式的左侧推断类型,或者从右侧推断左侧的类型,则会更有用。后者是发生在斯卡拉。
Diamond运算符的要点只是在声明泛型类型时减少代码类型。它对运行时没有任何影响。
如果在Java 5和6中指定了唯一的区别,
号
必须将@SuppressWarnings("unchecked")指定为list(否则将收到未选中的强制转换警告)。我的理解是钻石运营商正在努力使开发变得更容易。它与泛型的运行时执行完全无关。
- 你甚至不必使用那个注释。至少在Eclipse中,您可以告诉编译器不要对此发出警告,这样就可以了…
- 最好有注释。并非所有开发人员都在这里使用Eclipse。