Java中的所有通配符都可以替换为非通配符类型吗?

时间:2011-09-06 08:47:39

标签: java generics wildcard

我找不到任何通配符不能替换通配符的示例。 例如:

 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);

所以我不承认为什么要​​创建通配符,因为泛型已经在完成这项工作。有什么想法或意见吗?

3 个答案:

答案 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);
}