2013-07-17 62 views
6

此代碼會導致用javac編譯錯誤(但是,值得注意的是,不使用Eclipse 4.2.2!):爲什麼在這裏不允許轉換爲「GenericType <?>」?

public interface Foo<T> { 
} 

class Bar<T> implements Foo<Iterable<T>> { 
} 

class Test { 
    void test(Foo<? extends Iterable<? extends String>> foo) { 
     Bar<?> bar = (Bar<?>) foo; 
    } 
} 

從javac的錯誤是這樣的:

Foo.java:9: error: inconvertible types 
     Bar<?> bar = (Bar<?>) foo; 
          ^
    required: Bar<?> 
    found: Foo<CAP#1> 
    where CAP#1 is a fresh type-variable: 
    CAP#1 extends Iterable<? extends String> from capture of ? extends Iterable<? extends String> 

更改強制轉換爲(Bar) foo (即使用原始類型)允許編譯代碼,也可以將foo的類型更改爲Foo<? extends Iterable<?>>

編輯:歡快的,這個簡單的變化使得Eclipse的拒絕,但javac的接受:

void test(Foo<Iterable<String>> foo) { 
    Bar<?> bar = (Bar<?>) foo; 
} 

而且,Eclipse和javac的拒絕此一:

void test(Foo<Iterable<? extends String>> foo) { 
    Bar<?> bar = (Bar<?>) foo; 
} 
+0

希望你永遠不必像這樣做任何醜陋的鑄造,這只是一個好奇的問題... – jahroy

+0

是的只是好奇心。真實生活中的例子實際上是「instanceof Bar」工作但「instanceof Bar 」沒有的檢查實例。 –

+0

這裏有一些提及,http://docs.oracle.com/javase/tutorial/java/generics/capture.html –

回答

2

如果程序員想要明確地將類型X轉換成Y類型,則假定程序員比編譯器知道的更好,該語言可以允許它。

但是Java中的好人們想要阻止一些明顯不可能的演員,例如, (Cat)dog。所以他們有這個致力於這個主題的這個光榮的詳細部分 - http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.5.1

但是,這些規則是否有任何問題?而且,編譯器是否符合這些規則? ......這些問題太複雜,不太有趣。

我們應該關心的是演員是否有意義;如果它確實有道理,並且編譯器拒絕接受它,沒有問題,只需解決它。


在你的問題中,有插入通配符,在Foo< >兩個地方,並在Iterable< >。在每一個地方,通配符可以

0. None 
    1. Bounded "? extends Something" 
    2. Unbounded "?" 

讓我們探索所有的組合,這是我的結論是:

 wildcard#1 wildcard#2  should_compile javac  eclipse 

00 -    -    Y    Y   N 
01 -    ? extends  N    N   N 
02 -    ?    N    N   N 
10 ? extends  -    Y    N   Y 
11 ? extends  ? extends  Y    N   Y 
12 ? extends  ?    Y    Y   Y 
20 ?    -    Y    Y   Y 

should_compile意味着投有道理,還是沒有,稍後解釋。

如果10 and 11,代碼應該編譯,但javac拒絕它。規則有問題,或者javac有錯誤。


讓我們來看看,例如,爲什麼情況00有意義,應編譯

void test00(Foo<Iterable<String>> foo) { 
    Bar<?> bar = (Bar<?>) foo; 
} 

the question is, could there be a class/interface `X`, such that 
     Bar<X> <: Foo<Iterable<String>> 
=> Foo<Iterable<X>> <: Foo<Iterable<String>> 
=> Iterable<X> = Iterable<String> 
=> X = String 
so the answer is yes, the cast makes sense. 

,爲什麼情況01不應該編譯

 Foo<Iterable<X>> <: Foo<Iterable<? extends String>> 
=> Iterable<X> = Iterable<? extends String> 
=> NO SOLUTION 
    note that Iterable<String> != Iterable<? extends String> 

和案例11

 Foo<Iterable<X>> <: Foo<? extends Iterable<? extends String>> 
=> Iterable<X> <: Iterable<? extends String> 
=> X <: String 

令人驚訝的是,案件01不應編譯,雖然它感覺合理。根本問題是,Iterable是非常有效的,我們應該在它使用的任何地方使用通配符。所以理想情況下,我們應該聲明

class Bar<T> implements Foo<Iterable<? extends T>> 

但是如果我們插入通配符到處都是生命。更好的解決方案是聲明網站差異。不確定Java是否會在我們全部退休之前添加該功能。

+0

我對案例01感到驚訝。事實上,我很驚訝,這是無效的:「Foo > foo = new Foo >();「 –

+0

Ohhhh哇,好吧,我沒有意識到Foo >是一個完整的類型。 –

+0

@TavianBarnes是的,嵌套通配符與頂級通配符是非常不同的:http://stackoverflow.com/questions/3546745/multiple-wildcards-on-a-generic-methods-makes-java-compiler-and-me-非常confu/3547372#3547372 –

4

你的代碼是合理的,應該編譯:您正在將靜態類型Foo縮小爲Bar,同時將通用類型? extends Iterable<? extends String>擴展爲?,其中頂級?是通配符捕獲。這是一個checked cast,它應該在沒有警告的情況下編譯。

您應該submit an Oracle bug。我試圖搜索相應的錯誤報告,但this old closed ticket是我能找到的最接近的問題(IMO錯誤數據庫非常難以搜索)。當我有機會時我會搜索更多,因爲這個javac問題感覺非常熟悉。

相關問題