在通用类下有以下两种方法:
public class Container<S> {
public void f2(List<Object> l1, List<?> l2) {
l1 = l2; //compilation error row #1
}
public void f3(List<?> c, List<S> l) {
c = l; //ok row #2
l = c; //compilation error row #3
}
}
我真的不懂,为什么第二行还可以-如果我将此方法转移两个列表,一个是对象类型列表,一个是字符串列表,我应该得到编译错误吗?
真的很想知道为什么/不应该编译每一行。
答案 0 :(得分:3)
为什么要编译?
List<?> c, List<S> l;
c = l; // OK
List<?>
表示(或多或少)“某物的列表”(或更正式地为“未知物的列表”),而List<S>
就是其中之一。
-
为什么不编译?
List<Object> l1, List<?> l2;
l1 = l2; //compilation error
如果允许,则可以将任何内容(例如字符串)添加到l1
中,但是如果l2
是List<Object>
之外的任何内容(例如Integer),则表示l2
中错误的类型。这就是不允许此分配的原因。
另一种编译错误更细微,并且也没有用例-也就是说,没有理由将类型化列表分配给未类型化列表-但是通配符?
的真正含义是“未知,但特定”。这与“任何东西”都不相同。是“某物”,但我们不知道是什么。类型S
是什么,但是编译器无法验证它与S
是 一样。
答案 1 :(得分:1)
泛型和通配符功能强大,因为它们可确保在编译时进行更好的类型检查,这是使用它们的主要目的。为了确保您的代码不会在运行时由于类型检查不佳而中断。
尽管Integer是Number的子类型,
List<Integer>
不是List<Number>
的子类型,实际上,这两种类型无关。List<Number>
和List<Integer>
的共同父级是List<?>
。为了在这些类之间创建关系,以便代码可以通过
Number
的元素访问List<Integer>
的方法,请使用上限通配符:List<? extends Integer> intList = new ArrayList<>(); List<? extends Number> numList = intList; // OK. List<? extends Integer> is a subtype of List<? extends Number>
由于
Integer
是Number
的子类型,并且numList
是Number
对象的列表,因此intList
之间存在一个关系(Integer
个对象)和numList
。下图显示了使用上下界通配符声明的几个List类之间的关系。
要对通配符使用子类型化和多态性,必须使用有界通配符。
public void f2(List<Object> l1, List<?> l2) {
l1 = l2; //compilation error row #1
}
//correct way of doing it
public void f2(List<? extends Object> l3, List<?> l4) {
l3 = l4;
}
编译器不会将l2视为l1的子类型
public void f3(List<?> c, List<S> l) {
c = l; //ok row #2
l = c; //compilation error row #3
}
编译器不会将c视为l的子类型(正确的做法是,这可能会导致运行时错误)。