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();
我对类型擦除的理解是它们完全相同。(泛型在运行时被移除了)。
那为什么还要使用钻石语法呢?它有什么新功能/类型安全性吗?如果没有带来任何新功能,为什么会将其作为一个特性进行提及呢?我的这个概念理解有误吗?
问题在于
List<String> list = new LinkedList();
你的代码左边使用了泛型类型 List<String>
,而右边使用了原始类型 LinkedList
。在Java中,原始类型仅为与泛型前代码兼容而存在。除非必须这样做,否则不应在新代码中使用它们。
如果Java一开始就有泛型,并且没有像 LinkedList
这样在泛型出现之前创建的类型,它可能会使泛型类型的构造函数根据赋值语句左侧自动推断其类型参数。但事实并非如此,为了向后兼容,Java必须对待原始类型和泛型类型有所区别。这导致需要采用一个稍微不同但同样方便的方式来声明泛型对象的新实例,而无需重复其类型参数...钻石操作符。
至于你最初的代码示例 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
的无参构造函数!
List<String> strings = new List<>()
是可以的,但如果你先定义了 private List<String> my list;
,然后在页面的中间位置再使用 my_list = new List<>()
进行实例化,那么就不太好了!我得再找一下这个 my list 到底包含了什么内容。这时候,diamond 的快捷方式所带来的好处就消失了。 - rmirabellemy_list = getTheList()
有何不同?有几种更好的方法来处理这种问题:1. 使用可以在鼠标悬停时显示变量类型的 IDE。2. 使用更有意义的变量名称,例如 private List<String> strings
。3. 除非确实需要,否则不要将变量的声明和初始化分开。 - Natix你的理解有些错误。钻石操作符是一项好功能,因为你不必重复自己。当你声明类型时只需要定义一次类型,但在右边再次定义就没有意义了。这是DRY原则。
现在来解释一下定义类型的所有模糊问题。你说得对,在运行时类型被移除,但是一旦你想要从具有类型定义的列表中检索出某些东西,你会将其作为你在声明列表时定义的类型返回,否则它会失去所有特定的特征,并且只有Object的特征,除非你将检索到的对象强制转换为其原始类型,这有时可能非常棘手,并导致ClassCastException。
使用List<String> list = new LinkedList()
会得到原始类型警告。
<?>
)。此外,无用的尖括号语法应该不存在。 - maaartinus这行代码会产生[unchecked]警告:
List<String> list = new LinkedList();
所以,问题转化为:为什么只有在创建新集合的情况下未经检查的警告才不会自动抑制?
我认为,这比添加<>功能要困难得多。
更新:我也认为,如果可以使用原始类型“仅用于一些事情”,那么可能会出现混乱。package 7 com.example;
你可以更加清晰地阐述(可能更喜欢使用一个或多个花哨的关键词)。这甚至可以让不同Java版本编写的源代码一起编译而没有任何问题。它将允许引入新的关键字(例如,“module”)或删除某些已过时的特性(例如单个文件中的多个非公共非嵌套类等),而不会失去任何兼容性。
new ArrayList(anotherList)
和 new ArrayList<>(anotherList)
之间的区别(特别是当它被分配给 List<String>
,而 anotherList
是一个 List<Integer>
时)? - Paul Bellora<
和>
被过滤掉了,难以理解其含义。我想说的是,如果我写下这样一行代码 List<Foo> list=new ArrayList();
,编译器假定我想要一个裸类型,而事实上变量的类型List<Foo>
明确说明它不是旧的代码(并明确表示我不想要一个裸类型,否则我会写List list=…
)。这没有任何合理的理由去支持先前的泛型代码。 - Holgerf(new List())
可能会更加复杂,但我猜想你实际上从来不需要行类型,除非你明确要求它(例如使用你的 List list=
)。我不确定所有的边缘情况,但我和你一样。+++ 尽管如此,假设需要一个特殊的语法,并且是我发明原始类型构造函数的语法,我会使用 new List<>()
。:D - maaartinusnew @RawType List()
的东西呢?这已经是有效的Java 8语法,类型注释允许在每个需要的地方使用它,例如 @RawType List = (@RawType List) genericMethod();
。考虑到原始类型目前会创建编译器警告,除非放置了适当的 @SuppressWarnings
,@RawType
将是一个合理的替代选择,而不需要更微妙的语法。 - HolgerList<String> list = new LinkedList();
时,编译器会产生一个“unchecked”警告。你可以忽略它,但如果你习惯忽略这些警告,你也可能会错过一个通知你真正类型安全问题的警告。其他回答中提到的都是正确的,但在我看来使用情况并不完全有效。如果查看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中。
钻石操作符的作用是在声明泛型类型时减少代码输入,对运行时没有任何影响。
在Java 5和6中,唯一的区别是您是否指定了类型参数:
List<String> list = new ArrayList();
问题在于您必须为list
指定@SuppressWarnings("unchecked")
(否则会收到未经检查的转换警告)。我理解钻石操作符试图使开发更加容易。它与泛型的运行时执行完全无关。