Java wildcard in multi
Why Bar.go
is OK
with argument f2
but not with argument 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) {
}
}
Shouldn't the compiler automatically infer type T
as capture of ?
in both cases?
Great question!
(In the following comments, wrt a class generic in E
like Foo< E >
, define "covariant method" as a method that returns an E
without having any parameters using E
, and a "contravariant method" as the opposite: one which takes a formal parameter of type E
but doesn't return a type involving E
. [The real definition of these terms is more complicated, but never mind that for now.])
It seems that the compiler is trying to bind T
to Object
in the case of f1
, because if you do
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
}
}
then the go(f1)
works, but now go(f2)
doesn't, because even though Foo< String > <: Foo< ? extends String >
Foo< String > <: Foo< ? extends String >
, that does not imply that Foo< Foo< String > > <: Foo< Foo< ? extends String > >
Foo< Foo< String > > <: Foo< Foo< ? extends String > >
.
Here are a few modifications that compile for both f1
and 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<?>>();
This implies that the type is unknown and objects of any type can be added to Foo<Foo<?>>
that are heterogeneous and compiler cannot guarantee that all object in Foo<Foo<?>>
are of same type. Hence it cannot be passed to Bar.go
that takes a bounded type as parameter.
You can instead declare that as Foo<Foo<Object>> f1 = new Foo<Foo<Object>>();
to pass it to Bar.go
where you explicitly mention everything is of type Object
.
A Nice Read What do multi-level wildcards mean?
Example:
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
Of course, we can assign a Collection<Pair<String,Long>>
to a Collection<Pair<String,Long>>
. There is nothing surprising here.
But we can not assign a Collection<Pair<String,Long>>
to a Collection<Pair<String,?>>
. The parameterized type Collection<Pair<String,Long>>
is a homogenous collection of pairs of a String and a Long ; the parameterized type Collection<Pair<String,?>>
is a heterogenous collection of pairs of a String and something of unknown type. The heterogenous Collection<Pair<String,?>>
could for instance contain a Pair<String,Date>
and that clearly does not belong into a Collection<Pair<String,Long>>
. For this reason the assignment is not permitted.
上一篇: 任何等效的“扩展”的C#?
下一篇: 多通用Java通配符