多通用Java通配符
为什么Bar.go
OK
使用参数f2
但不使用参数f1
?
public class HelloWorld {
public static void main(String[] args) {
Foo<Foo<?>> f1 = new Foo<Foo<?>>();
Foo<Foo<String>> f2 = new Foo<Foo<String>>();
Bar.go(f1); // not OK
Bar.go(f2); // OK
}
public static void p(Object o) {
System.out.println(o);
}
}
class Foo<E> {
}
class Bar {
public static <T> void go(Foo<Foo<T>> f) {
}
}
编译器是否应该自动推断类型T
为capture of ?
在这两种情况下?
伟大的问题!
(在下面的注释中,像Foo< E >
那样在E
有一个泛型类,将“协变方法”定义为返回E
而不使用E
任何参数的方法,以及“逆变法”作为相反的方法: E
类型的形式参数,但不返回涉及E
的类型[这些术语的真正定义比较复杂,但现在不用介意])
看起来,编译器正试图在f1
的情况下将T
绑定到Object
,因为如果你这样做的话
class Bar0 {
public static < T > void go( Foo< Foo< ? extends T > > f ) {
// can pass a Foo< T > to a contravariant method of f;
// can use any result r of any covariant method of f,
// but can't pass T to any contravariant method of r
}
}
那么go(f1)
有效,但现在go(f2)
不会,因为即使Foo< String > <: Foo< ? extends String >
Foo< String > <: Foo< ? extends String >
,这并不意味着Foo< Foo< String > > <: Foo< Foo< ? extends String > >
Foo< Foo< String > > <: Foo< Foo< ? extends String > >
。
以下是针对f1
和f2
编译的一些修改:
class Bar1 {
public static < T > void go( Foo< ? super Foo< T > > f ) {
// can't properly type the results of any covariant method of f,
// but we can pass a Foo< T > to any contravariant method of f
}
}
class Bar2 {
public static < T > void go( Foo< ? extends Foo< ? extends T > > f ) {
// can't pass a Foo< T > to a contravariant method of f;
// can use result r of any covariant method of f;
// can't pass a T to a contravariant method of r;
// can use result of covariant method of r
}
}
Foo<Foo<?>> f1 = new Foo<Foo<?>>();
这意味着该类型是未知的,并且任何类型的对象都可以被添加到异构的Foo<Foo<?>>
,并且编译器不能保证Foo<Foo<?>>
中的所有对象都是相同类型的。 因此它不能传递给Bar.go
,它将一个有界类型作为参数。
你可以改为声明为Foo<Foo<Object>> f1 = new Foo<Foo<Object>>();
将其传递给Bar.go
,您明确提到所有内容都是Object
类型的。
很好的阅读什么是多级通配符?
例:
Collection< Pair<String,Long> > c1 = new ArrayList<Pair<String,Long>>();
Collection< Pair<String,Long> > c2 = c1; // fine
Collection< Pair<String,?> > c3 = c1; // error
Collection< ? extends Pair<String,?> > c4 = c1; // fine
当然,我们可以将Collection<Pair<String,Long>>
分配给Collection<Pair<String,Long>>
。 这里没有什么奇怪的。
但是我们不能将Collection<Pair<String,Long>>
赋值给Collection<Pair<String,?>>
。 参数化类型Collection<Pair<String,Long>>
是String和Long的对的同类集合; 参数化类型Collection<Pair<String,?>>
是String和某些未知类型对的异构集合。 异构Collection<Pair<String,?>>
可以包含一个Pair<String,Date>
,并且显然不属于Collection<Pair<String,Long>>
。 出于这个原因,分配是不允许的。