我找不到任何通配符不能替换通配符的示例。 例如:
public void dummy(List<? extends MyObject> list);
相当于
public <T> void dummy(List<T extends MyObject> list);
或
public <T> List<? extends T> dummy2(List<? extends T> list);
相当于
public <T, U> List<U extends T> dummy(List<U extends T> list);
所以我不承认为什么要创建通配符,因为泛型已经在完成这项工作。有什么想法或意见吗?
答案 0 :(得分:7)
不,它并不总是可以替换的。
List<? extends Reader> foo();
不等于
<T> List<T extends Reader> foo();
因为您在调用T
时不知道foo()
(并且您无法知道List<T>
将foo()
返回什么。在您的第二个示例中,同样的事情发生了,太
demonstration of using wildcards can be found in this (my) answer。
答案 1 :(得分:2)
一个简单的答案是,更深层次的通配符不能被类型变量替换
void foo( List<List<?>> arg )
与
非常不同<T>
void foo( List<List<T>> arg)
这是因为通配符捕获转换仅适用于第1级通配符。我们来谈谈这些。
由于广泛的捕获转换,在大多数地方,编译器将通配符视为类型变量。因此,程序员确实可以在这些地方用类型变量替换通配符,这是一种手动捕获转换。
由于程序员无法访问编译器为捕获转换创建的类型变量,因此这具有@josefx提到的限制效果。例如,编译器将List<?>
对象视为List<W>
对象;因为W
是编译器内部的,虽然它有一个方法add(W item)
,但程序员无法调用它,因为他没有W
类型的项。但是,如果程序员“手动”将通配符转换为类型变量T
,则可以使用类型为T
的项目并在其上调用add(T item)
。
另一种相当随机的情况,即通配符不能替换为类型变量:
class Base
List<? extends Number> foo()
class Derived extends Base
List<Integer> foo()
这里,foo()被一个协变返回类型覆盖,因为List<Integer>
是List<? extends Number
的子类型。如果使用类型变量替换通配符,则无效。
答案 2 :(得分:0)
您的示例存在使用差异。
public <T> List<? extends T> dummy2(List<? extends T> list);
返回一个可以包含未知子类型T的列表,这样就可以从中获取T类型的对象,但不能添加它。
示例T =数字
List<? extends Number> l = new ArrayList<Integer>(Arrays.asList(new Integer(1)));
//Valid since the values are guaranteed to be a subtype of Number
Number o = l.get(0);
//Invalid since we don't know that the valuetype of the list is Integer
l.add(new Integer(1));
因此通配符可用于使某些操作无效,API可以使用它来限制接口。
示例:
//Use to remove unliked numbers, thanks to the wildcard
//it is impossible to add a Number
@Override public void removeNumberCallback(List<? extends Number> list){
list.remove(13);
}