请说明List
- 原始类型与List<Object>
之间的区别。
以下代码给出了运行时错误:
public static void main(String[] args) {
List<String> strings = new ArrayList<String>();
unsafeAdd(strings, new Integer(42));
String s = strings.get(0); // Compiler-generated cast
}
private static void unsafeAdd(List list, Object o) {
list.add(o);
}
这会产生编译时错误:
public static void main(String[] args) {
List<String> strings = new ArrayList<String>();
unsafeAdd(strings, new Integer(42));
String s = strings.get(0); // Compiler-generated cast
}
private static void unsafeAdd(List<Object> list, Object o) {
list.add(o);
}
答案 0 :(得分:7)
在第二种情况下,你正在做一些编译器可以锻炼的东西并不安全。在第一种情况下,您使用的是原始类型,因此编译器不会执行相同的检查。
答案 1 :(得分:2)
Java没有参数类型的继承。因此List<Integer>
不是List<Object>
的子类,因此您无法使用List<Integer>
或List<String>
作为unsafeAdd
方法的参数。但你可以写:
private static <T> void unsafeAdd(List<T> list, T o) {
list.add(o);
}
和safelly称之为:
List<String> strings = ...
unsafeAdd(string, "42");
并在以下情况下收到错误:
List<String> strings = ...
unsafeAdd(strings, 42); // error!
您可以在Oracle Generics Tutorial, Generics, Inheritance, and Subtypes
中查看更多信息答案 2 :(得分:0)
在第一种情况下,您将未参数化的List传递给unsafeAdd,因此编译器无法弄清楚出错的地方。
将List<String>
传递给期望List
的方法即可。将对象添加到List
是可以的。
在第二种情况下,您将List<String>
传递给期望List<Object>
的方法 - 这不是正常的。因为这种方式暗示允许将非String添加到List<String>
- 编译器可以在这种情况下解决它并引发错误。
答案 3 :(得分:0)
正如彼得已经说过的那样,在第一个例子中,你告诉编译器使用原始类型,因此不对列表执行任何检查。因此,它允许您将任何对象添加到传递的列表中,即使它被定义为仅允许字符串。
在第二个示例中,您告诉编译器它应该假定列表允许任何对象,因此add操作将编译。但是,不允许将List<String>
作为List<Object>
传递,因为字符串列表具有比仅作为对象的内容更具体的限制,因此编译器知道这是不安全且容易出错的。 / p>
如果您将参数定义为List<? extends Object> list
,编译器会接受传递List<String>
,因为您告诉它最小要求是列表必须接受对象,但它也可能施加更严格的约束。但是,add操作现在不会编译,因为编译器不知道 if 是否存在更严格的约束,如果是,那么这些约束是什么。因此,它无法确保添加操作是安全的并且拒绝编译该行。
答案 4 :(得分:0)
然而它看起来是一样的,它不是。
List
(原始类型)并不关心你插入的内容。
List<String>
与List<Object>
不兼容。你可以这样做:
String s = "Hello";
Object o = s;
但你不能这样做:
List<String> ls = ...;
List<Object> lo = ls; // Compilation error!
您的示例是一个很好的例证为什么 Java语言不允许它。通过允许这样的分配,恶意方法可以将任何内容放入客户端认为是字符串列表的列表中。
请考虑以下代码:
public void method changeObject(Object o) {
o = 42;
}
public void method changeList(List<Object> lo) {
lo.add(42);
}
...
String str = "Hello";
changeObject(str);
// Here it is still safe to use str, its value has not been modified
String str2 = str; // OK
List<String> list = new ArrayList<>();
changeList(list); // it is not allowed, but let's assume it is
// here the list variable would violate its contract - everybody has a right
// to expect it is a list of Strings
String str3 = list.get(0); // Ouch! :(