开发者

Can all wildcards in Java be replaced by non-wildcard types?

I can't find any example where wildcards can't be replaced by a generic. For example:

 public void dummy(List<? extends MyObject> list);

is equivalent 开发者_StackOverflowto

 public <T> void dummy(List<T extends MyObject> list);

or

 public <T> List<? extends T> dummy2(List<? extends T> list);

is equivalent to

 public <T, U>  List<U extends T> dummy(List<U extends T> list);

So I don't undertand why wildcard was created as generics is already doing the job. Any ideas or comments?


Nope, it is not always replaceable.

List<? extends Reader> foo();

is not equivalent to

<T> List<T extends Reader> foo();

because you don't know the T when calling foo() (and you cannot know what List<T> will foo() return. The same thing happens in your second example, too.

The demonstration of using wildcards can be found in this (my) answer.


An easy answer is that, deeper level wildcards can't be replaced by a type variable

void foo( List<List<?>> arg )

is very different from

<T> 
void foo( List<List<T>> arg)

This is because wildcard capture conversion is only applied to 1st level wildcards. Let's talk about these.

Due to extensive capture conversion, in most places, compiler treats wildcards as if they are type variables. Therefore indeed programmer can replace wildcard with type variables in such places, a sort of manual capture conversion.

Since a type variable created by compiler for capture conversion is not accessible to programmer, this has the restricting effect mentioned by @josefx. For example, compiler treats a List<?> object as a List<W> object; since W is internal to compiler, although it has a method add(W item), there's no way for programmer to invoke it, because he has no item of type W. However, if programmer "manually" converts the wildcard to a type variable T, he can have an item with type T and invoke add(T item) on it.

Another rather random case where wildcard can't be replaced type variable:

class Base
    List<? extends Number> foo()

class Derived extends Base
    List<Integer> foo()

Here, foo() is overriden with a covariant return type, since List<Integer> is a subtype of List<? extends Number. This won't work if the wildcard is replaced with type variable.


There is a usage difference for your examples.

public <T> List<? extends T> dummy2(List<? extends T> list);

returns a list that can contain an unknown subtype of T so you can get objects of type T out of it but can't add to it.

Example T = Number

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

So the wildcard can be used to make some operations invalid, this can be used by an API to restrict an interface.

Example:

//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);
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜