2014-02-08 30 views
19

我認識的Java 8仍處於測試階段,但這個讓我覺得奇怪:的Java 8需要鑄造,而Java 7沒有 - enum.getClass/getDeclaringClass

public class Fields<C extends Enum<C>> { 

    public Fields(Set<C> columns) { 
     // A sample column used to find the universe of the enum of Columns. 
     C sampleCol = columns.iterator().next(); 
     // Java 8 needs a cast here. 
     Set<C> allColumns = EnumSet.allOf((/*Class<C>)*/ sampleCol.getClass()); 
     // ... there's more to this that I've deleted. 
    } 

} 

錯誤讀取:

error: incompatible types: inferred type does not conform to equality constraint(s) 
      Set<C> allColumns = EnumSet.allOf(sampleCol.getClass()); 
    inferred: C 
    equality constraints(s): C,CAP#1 
    where C is a type-variable: 
    C extends Enum<C> declared in class Test.Fields 
    where CAP#1 is a fresh type-variable: 
    CAP#1 extends Enum from capture of ? extends Enum 

這是Java 8的一個缺陷還是一個新特性?

+0

我試過用Java 7(51)和Java 8(b128),並且在這兩種情況下編譯器都需要強制轉換,當然兩種情況下仍然處於警告狀態。但是,當我移除鑄件時,在兩種情況下都會出現錯誤(Java 7,8)。我在IDEA 13環境中嘗試了它。 –

回答

18

有趣的是,這是治療raw types的微妙變化。

首先,讓我們來澄清你的例子。的Object.getClass返回類型是特殊的:

實際結果類型爲Class<? extends |X|>其中|X|是靜態類型上getClass被稱爲表達的擦除。

在這種情況下,X將類型參數C,其erasesEnum。所以sampleCol.getClass()返回Class<? extends Enum>EnumSet.allOf聲明瞭類型參數E extends Enum<E>,在你的情況下,? extends Enum被推斷爲它的類型參數。

重要的部分是Enum是一種原始類型。原始類型的使用已被視爲抹去看似無關的泛型,例如在這篇文章中:Why won't this generic java code compile? Jon Skeet在他的回答中引用了JLS §4.8(「原始類型」)來涵蓋這種不直觀的行爲。

類似的行爲似乎與Java 7的例子是發生:EnumSet.allOf(sampleCol.getClass())被允許以「未選中調用」警告編譯(這被通過後續的「未轉換」隱藏從指定警告所產生的原始EnumSetSet<C>) 。

問題是:通用通配符邊界中的原始類型的出現是否允許未經檢查的轉換? JLS§4.8沒有提到這一點,所以它是不明確的。可能這是一個錯誤,但它似乎是一個合理的收緊這種行爲。雖然像Enum這樣的標準原始類型本身可能需要傳統的API,但像Class<? extends Enum>這樣的「半成品」類型只能出現在後泛型上,所以讓它破壞泛型類型檢查並不合乎情理。

無論如何,我很感興趣,看看有沒有人可以指出關於這個改變的文檔 - 我的搜索沒有改變任何東西。


關於你的具體代碼:你應該使用getDeclaringClass()來代替。編譯器無法知道在C上調用getClass將確切返回Class<C>;事實上,如果在具有常量特定類的枚舉上使用它,它將不會被使用。這正是Enum聲明該方法的用例。

+0

感謝您的見解和解決方案 - 非常有趣。奇怪的是,這是我需要使用'getDeclaringClass'這一週的第二次 - 每個都有完全不同的情況。我以前從未需要它。 – OldCurmudgeon