[英]Java wildcard in multi-level generic type
Why Bar.go
is OK
with argument f2
but not with argument f1
? 为什么
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) {
}
}
Shouldn't the compiler automatically infer type T
as capture of ?
不应该编译器自动推断类型
T
为capture of ?
in both cases? 在这两种情况下?
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. 这意味着类型是未知的,任何类型的对象都可以添加到异构的
Foo<Foo<?>>
,编译器不能保证Foo<Foo<?>>
中的所有对象都是相同的类型。 Hence it cannot be passed to Bar.go
that takes a bounded type as parameter. 因此,它无法传递给采用有界类型作为参数的
Bar.go
You can instead declare that as Foo<Foo<Object>> f1 = new Foo<Foo<Object>>();
你可以改为声明为
Foo<Foo<Object>> f1 = new Foo<Foo<Object>>();
to pass it to Bar.go
where you explicitly mention everything is of type Object
. 将它传递给
Bar.go
,你明确提到一切都是Object
类型。
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.]) (在下面的评论,WRT一类通用的在
E
等Foo< E >
中,定义“协变方法”作为返回的方法E
不使用具有任何参数E
,以及“逆变方法”为相反:一个这需要类型E
的形式参数,但不返回涉及E
的类型。[这些术语的真正定义更复杂,但现在不记得了。])
It seems that the compiler is trying to bind T
to Object
in the case of f1
, because if you do 在
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
}
}
then the go(f1)
works, but now go(f2)
doesn't, because even though Foo< String > <: Foo< ? extends String >
然后
go(f1)
工作,但现在go(f2)
没有,因为即使Foo< String > <: Foo< ? extends String >
Foo< String > <: Foo< ? extends String >
, that does not imply that Foo< Foo< String > > <: Foo< Foo< ? extends String > >
Foo< String > <: Foo< ? extends String >
,这并不意味着Foo< Foo< String > > <: Foo< Foo< ? extends String > >
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
: 以下是为
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
}
}
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>>
. 当然,我们可以将
Collection<Pair<String,Long>>
分配给Collection<Pair<String,Long>>
。 There is nothing surprising here. 这里没有什么可惊讶的。
But we can not assign a Collection<Pair<String,Long>>
to a Collection<Pair<String,?>>
. 但是我们不能将
Collection<Pair<String,Long>>
分配给Collection<Pair<String,?>>
。 The parameterized type Collection<Pair<String,Long>>
is a homogenous collection of pairs of a String and a Long ; 参数化类型
Collection<Pair<String,Long>>
是String和Long对的同类集合; the parameterized type Collection<Pair<String,?>>
is a heterogenous collection of pairs of a String and something of unknown type. 参数化类型
Collection<Pair<String,?>>
是一组String和一些未知类型的异构集合。 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>>
. 异构
Collection<Pair<String,?>>
可以包含一个Pair<String,Date>
,它显然不属于Collection<Pair<String,Long>>
。 For this reason the assignment is not permitted. 因此,不允许分配。
I will prove that if the compiler allows Bar.go(f1);
我将证明,如果编译器允许
Bar.go(f1);
, the type system(safety) would be broken: ,类型系统(安全)将被打破:
Java grammar allows you to use T
as a type to declare variables in go()
. Java语法允许您使用
T
作为类型来在go()
声明变量。 Something like: T t = <something>
. 类似的东西:
T t = <something>
。
Now, let's use ArrayList
instead of Foo
, 现在,让我们使用
ArrayList
而不是Foo
,
Then we have: 然后我们有:
class HW {
public static void main(String[] args) {
ArrayList<ArrayList<?>> f1 = new ArrayList<ArrayList<?>>();
go(f1); // not OK
}
public static <T> void go(ArrayList<ArrayList<T>> f) {
}
}
ArrayList<?>
is supertype of ArrayList<String>
, it is also supertype of ArrayList<Integer>
, meaning that you can do the following in main
: ArrayList<?>
是ArrayList<String>
超类型,它也是ArrayList<Integer>
超类型,这意味着您可以在main
执行以下操作:
ArrayList<?> s = new ArrayList<String>();
f1.add(s);
ArrayList<?> i = new ArrayList<Integer>();
f1.add(i);
Now, let's assume that the compiler allows you to call go()
with f1
as argument. 现在,让我们假设编译器允许您使用
f1
作为参数调用go()
。 The option to infer T
are: 推断
T
的选项是:
T = Object
, but ArrayList<ArrayList<Object>>
is not ArrayList<ArrayList<?>>
because ArrayList<Object>
is not the same type as ArrayList<?>
So that is not allowed option. T = Object
,但ArrayList<ArrayList<Object>>
不是ArrayList<ArrayList<?>>
因为ArrayList<Object>
与ArrayList<?>
类型不同,所以不允许选项。
T = ?
, then we would be able to do: ,那么我们就能做到:
public static <T> void go(ArrayList<ArrayList<T>> f) { ArrayList<T> alt1 = f.get(0); // ArrayList<String> T str = alt1.get(0); ArrayList<T> alt2 = f.get(1); // ArrayList<Integer> alt2.add(str); // We have added String to List<Integer> // ... type system broken }
go()
to work in both cases you have to do: go()
在你必须做的两种情况下工作:
public static void go(ArrayList<? extends ArrayList<?>> f) {
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.