2012-09-21 29 views
1

我遇到一個奇怪的問題,用下面的代碼(當然,不是正是此代碼):好奇類型的錯誤

public class CompilationProblems1 { 

    static Box<Class<? extends AlcoholicBewerage>> brokenBoxOfOneBeer = boxOf(Beer.class); 
    static Box<? extends Class<? extends AlcoholicBewerage>> boxOfOneBeer = boxOf(Beer.class); 
    static Box<Class<? extends AlcoholicBewerage>> boxOfBeerAndVodka = boxOf(Beer.class, Vodka.class); 

    interface AlcoholicBewerage {} 

    class Beer implements AlcoholicBewerage {} 

    class Vodka implements AlcoholicBewerage {} 

    static class Box<T> {} 

    static <E> Box<E> boxOf(E e) { 
     return new Box<E>(); 
    } 

    static <E> Box<E> boxOf(E e1, E e2) { 
     return new Box<E>(); 
    } 
} 

第一個聲明brokenBoxOfOneBeer給出了一個編譯錯誤:

found : lt.tool.CompilationProblems1.Box<java.lang.Class<lt.tool.CompilationProblems1.Beer>> 
required: lt.tool.CompilationProblems1.Box<java.lang.Class<? extends lt.tool.CompilationProblems1.AlcoholicBewerage>> 
static Box<Class<? extends AlcoholicBewerage>> brokenBoxOfOneBeer = boxOf(Beer.class); 

這個錯誤發生在OpenJDK 6,Eclipse和IntelliJ上。我知道這是類型推理器的限制。

在第三種情況下(boxOfBeerAndVodka),編譯器能夠推斷正確的協變類型,因爲它有兩個可供選擇的子類型,我相信。但爲什麼不是編譯器能夠編譯第一個聲明,但是對於第二個聲明可以嗎?

回答

1

But why isnt' the compiler able to compile the first declaration, but is OK with the second one?

在這兩種情況下,表達式boxOf(Beer.class)具有類型Box<Class<Beer>>

  • 第一個聲明需要它有Box<Class<? extends AlcoholicBewerage>>;由於Box<Class<Beer>>不是Box<Class<? extends AlcoholicBewerage>>的子類型,因此不起作用。 (Class<Beer>Class<? extends AlcoholicBewerage>一個亞型,但由於不變性,這並不意味着Box<Class<Beer>>Box<Class<? extends AlcoholicBewerage>>子類型。)
  • 第二個聲明需要它具有類型Box<? extends Class<? extends AlcoholicBewerage>> —其它。 Class<Beer>Class<? extends AlcoholicBewerage>的一個亞型,ergo Box<Class<Beer>>Box<? extends Class<? extends AlcoholicBewerage>>的亞型。

也就是說,同樣的事情在你的聲明發生的會發生在:

List<Object> foo = new ArrayList<String>(); // doesn't work 
List<? extends Object> bar = new ArrayList<String>(); // works 

它只是看起來更加複雜,因爲不是ObjectString你有泛型類型的另一個層次。

+0

謝謝。當你考慮它的時候很明顯,而不是當實際的類型簽名比例子中更復雜時:) – dm3

1

首先根據實際參數(Beer.class)推斷出類型參數(§15.12.2.7)。如果根據實際參數無法推斷類型參數,則考慮上下文(brokenBoxOfBeer)(§15.12.2.8)。

因此,您可以按如下方式解決此問題。

static <X, E extends X> Box<X> boxOf(E e) { 
    return new Box<X>(); 
} 

編輯:我要指出,這是一個變通,並配有其自身的問題。嵌套調用將不再被正確推斷。

Box<Box<Integer>> x = boxOf(boxOf(1)); // won't compile 
+0

感謝您的回覆,但我已經批准了ruakh的答案,因爲它實際上回答了我的問題。 – dm3