java 7中的diamond操作符允许如下代码:
List<String> list = new LinkedList<>();
然而在Java 5/6中,我可以简单地写:
List<String> list = new LinkedList();
我对类型擦除的理解是它们是完全相同的。(无论如何泛型都会在运行时被删除)。
为什么要为钻石费心呢?它允许哪些新的功能/类型安全?如果它没有产生任何新的功能,他们为什么要把它作为一个特性来提及呢?我对这个概念的理解有缺陷吗?
java 7中的diamond操作符允许如下代码:
List<String> list = new LinkedList<>();
然而在Java 5/6中,我可以简单地写:
List<String> list = new LinkedList();
我对类型擦除的理解是它们是完全相同的。(无论如何泛型都会在运行时被删除)。
为什么要为钻石费心呢?它允许哪些新的功能/类型安全?如果它没有产生任何新的功能,他们为什么要把它作为一个特性来提及呢?我对这个概念的理解有缺陷吗?
这一行导致[unchecked]警告:
List<String> list = new LinkedList();
因此,问题转换为:为什么[unchecked]警告仅在创建新集合时才自动抑制?
我认为,这比添加<>特性要困难得多。
UPD:我还认为,如果只在少数方面合法地使用原始类型,将会造成混乱。
你的理解有点瑕疵。菱形操作符是一个很好的特性,因为你不需要重复自己。在声明类型时定义一次类型是有意义的,但在右侧再次定义它就没有意义了。DRY原则。
现在来解释定义类型的所有细节。你是对的,类型在运行时被删除,但一旦你想从一个带有类型定义的List中检索一些东西,你就会把它作为你在声明列表时定义的类型返回,否则它将失去所有特定的功能,只有Object功能,除非你将检索到的对象转换为它的原始类型,这有时会非常棘手,并导致ClassCastException。
使用List<String> List = new LinkedList()将得到rawtype警告。
当你写List<String> List = new LinkedList();,编译器产生一个“unchecked”警告。您可以忽略它,但是如果您习惯了忽略这些警告,那么您也可能会错过通知您实际类型安全问题的警告。
所以,最好写一段不产生额外警告的代码,菱形运算符可以让你以一种方便的方式做到这一点,而没有不必要的重复。
菱形操作符的作用仅仅是在声明泛型类型时减少代码的类型。它对运行时没有任何影响。
如果在Java 5和Java 6中指定,唯一的区别是,
List<String> list = new ArrayList();
你必须指定@SuppressWarnings("unchecked")到列表中(否则你将得到一个未检查的强制转换警告)。我的理解是,diamond operator正试图让开发变得更容易。它与泛型的运行时执行完全没有关系。
问题在于
List<String> list = new LinkedList();
在左边,你正在使用泛型类型List<String>,而在右边,你正在使用原始类型LinkedList。原始类型在Java中实际上只是为了与预泛型代码兼容而存在的,并且不应该在新代码中使用,除非 你必须这么做。
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.
对于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语言的通用部分……它们不仅仅适用于集合的无参数构造函数!
理论上,菱形操作符可以通过保存重复的类型参数来编写更紧凑(且可读)的代码。实际上,这只是两个令人困惑的字符,什么都没有。为什么?
理智的程序员不会在新代码中使用原始类型。因此,编译器可以简单地假设不写类型参数就可以推断它们。 菱形操作符不提供类型信息,它只是告诉编译器,“它会没事的”。所以省略它也无妨。在菱形操作符合法的任何地方,编译器都可以“推断”它。
在我看来,有一种清晰而简单的方法将源代码标记为Java 7将比发明这些奇怪的东西更有用。在这样标记的代码中,原始类型可以被禁止而不会损失任何东西。
顺便说一句。但是,我认为不应该使用编译开关。程序文件的Java版本是文件的一个属性,根本没有选项。使用像这样微不足道的东西
package 7 com.example;
你可能更喜欢一些更复杂的东西,包括一个或多个花哨的关键字。它甚至允许将为不同Java版本编写的源代码一起编译,而不会出现任何问题。它将允许引入新的关键字(例如,“module”)或删除一些过时的特性(单个文件中多个非公共非嵌套类或其他)而不失去任何兼容性。
其他回答中的所有回答都是有效的,但用例并不完全有效。如果你检查了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中发生的事情。