[英]Java wildcard in multi-level generic type
為什么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<Foo<?>> f1 = new Foo<Foo<?>>();
這意味着類型是未知的,任何類型的對象都可以添加到異構的Foo<Foo<?>>
,編譯器不能保證Foo<Foo<?>>
中的所有對象都是相同的類型。 因此,它無法傳遞給采用有界類型作為參數的Bar.go
你可以改為聲明為Foo<Foo<Object>> f1 = new Foo<Foo<Object>>();
將它傳遞給Bar.go
,你明確提到一切都是Object
類型。
好問題!
(在下面的評論,WRT一類通用的在E
等Foo< 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
}
}
一個很好的閱讀多級通配符是什么意思?
例:
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>>
。 因此,不允許分配。
我將證明,如果編譯器允許Bar.go(f1);
,類型系統(安全)將被打破:
Java語法允許您使用T
作為類型來在go()
聲明變量。 類似的東西: T t = <something>
。
現在,讓我們使用ArrayList
而不是Foo
,
然后我們有:
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<?>
是ArrayList<String>
超類型,它也是ArrayList<Integer>
超類型,這意味着您可以在main
執行以下操作:
ArrayList<?> s = new ArrayList<String>();
f1.add(s);
ArrayList<?> i = new ArrayList<Integer>();
f1.add(i);
現在,讓我們假設編譯器允許您使用f1
作為參數調用go()
。 推斷T
的選項是:
T = Object
,但ArrayList<ArrayList<Object>>
不是ArrayList<ArrayList<?>>
因為ArrayList<Object>
與ArrayList<?>
類型不同,所以不允許選項。
T = ?
,那么我們就能做到:
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()
在你必須做的兩種情況下工作:
public static void go(ArrayList<? extends ArrayList<?>> f) {
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.