簡體   English   中英

多級泛型類型中的Java通配符

[英]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) {
    }
}

不應該編譯器自動推斷類型Tcapture 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一類通用的在EFoo< 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 > >

以下是為f1f2編譯的一些修改:

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的選項是:

  1. T = Object ,但ArrayList<ArrayList<Object>>不是ArrayList<ArrayList<?>>因為ArrayList<Object>ArrayList<?>類型不同,所以不允許選項。

  2. 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM