对于两个 s 的实例化,一个在末端有菱形运算符,另一个没有...ArrayList
List<Integer> fooList = new ArrayList<>();
List<Integer> barList = new ArrayList();
...生成的字节码是相同的。
LOCALVARIABLE fooList Ljava/util/List; L1 L4 1
// signature Ljava/util/List<Ljava/lang/Integer;>;
// declaration: java.util.List<java.lang.Integer>
LOCALVARIABLE barList Ljava/util/List; L2 L4 2
// signature Ljava/util/List<Ljava/lang/Integer;>;
// declaration: java.util.List<java.lang.Integer>
因此,根据字节码,两者之间没有任何区别。
但是,如果您使用第二种方法,编译器将生成未选中的警告。因此,第二种方法实际上没有价值;您所要做的就是使用编译器生成一个未经检查的误报警告,这会增加项目的噪音。
我已经设法演示了一个场景,在这个场景中,这样做是非常有害的。它的正式名称是堆污染。这不是您希望在代码库中发生的事情,并且每当看到这种调用时,都应该将其删除。
请考虑以下类,它扩展了 的某些功能。ArrayList
class Echo<T extends Number> extends ArrayList<T> {
public Echo() {
}
public Echo(Class<T> clazz) {
try {
this.add(clazz.newInstance());
} catch (InstantiationException | IllegalAccessException e) {
System.out.println("YOU WON'T SEE ME THROWN");
System.exit(-127);
}
}
}
似乎无害;您可以添加绑定类型的任何实例。
但是,如果我们在玩原始类型...这样做可能会有一些不幸的副作用。
final Echo<? super Number> oops = new Echo(ArrayList.class);
oops.add(2);
oops.add(3);
System.out.println(oops);
这将打印而不是引发任何类型的异常。如果我们想对这个列表中的所有 s 执行一个操作,由于这个令人愉快的调用,我们会遇到一个 。[[], 2, 3]
Integer
ClassCastException
ArrayList.class
当然,如果添加钻石运营商,所有这些都可以避免,这将保证我们不会遇到这种情况。
现在,由于我们已经将原始类型引入到组合中,因此Java无法根据JLS 4.12.2执行类型检查:
例如,代码:
List l = new ArrayList<Number>();
List<String> ls = l; // Unchecked warning
导致编译时未选中警告,因为在编译时(在编译时类型检查规则的限制内)或在运行时,无法确定变量是否确实引用了 。l
List<String>
上述情况非常相似;如果我们看一下我们使用的第一个例子,我们所做的就是不要在这件事上添加一个额外的变量。堆污染是一样的。
List rawFooList = new ArrayList();
List<Integer> fooList = rawFooList;
因此,虽然字节码是相同的(可能是由于擦除),但事实仍然是,像这样的声明可能会产生不同或异常的行为。
不要使用原始类型,嗯?