2012-11-03 33 views
5

很抱歉的含糊稱號。我有這段代碼編譯基於Eclipse的Juno(4.2),但不是javac的(1.7.0_09):的Java CRTP和通配符:代碼編譯在Eclipse而不是`javac`

package test; 

public final class Test { 
    public static class N<T extends N<T>> {} 

    public static class R<T extends N<T>> { 
     public T o; 
    } 

    public <T extends N<T>> void p(final T n) {} 

    public void v(final R<?> r) { 
     p(r.o);  // <-- javac fails on this line 
    } 
} 

的錯誤是:

 
Test.java:13: error: method p in class Test cannot be applied to given types; 
     p(r.o); 
     ^
    required: T 
    found: N<CAP#1> 
    reason: inferred type does not conform to declared bound(s) 
    inferred: N<CAP#1> 
    bound(s): N<N<CAP#1>> 
    where T is a type-variable: 
    T extends N<T> declared in method <T>p(T) 
    where CAP#1 is a fresh type-variable: 
    CAP#1 extends N<CAP#1> from capture of ? 
1 error 

所以問題是:

  1. 這是一個javac錯誤或Eclipse的錯誤嗎?

  2. 有沒有辦法在javac上進行編譯,而不改變v方法的簽名(即保持通配符)?

    我知道它更改爲<T extends N<T>> void v(final R<T> r)確實使編譯,但我想知道是否有辦法避免這種第一。此外,該方法p不能改變<T extends N<?>> void p(final T n)因爲它的內容有哪些要求精確約束T extends N<T>類型。

+0

@Nambari:它也是1.7。此外,Eclipse不使用'javac'來編譯代碼,而是使用它自己的編譯器。 – kennytm

+0

它在我的eclipse中用java 6編譯你使用的是什麼java版本? –

+0

@AmitD:是的,它適用於Eclipse的兩個1.6和1.7,但它不會用的OpenJDK 6和7的javac,也不[太陽對ideone JDK 6](http://ideone.com/Z03W7V)編譯。 – kennytm

回答

6

通配符是有限的,因爲它們會破壞類型參數允許的遞歸表達式,如T extends X<T>。我們知道基於以下你想做什麼是安全的:

  1. r.oT型(R聲明),這是或擴展N<T>的。
  2. 方法p採用類型爲T(由p聲明)的參數,該參數也是或延伸爲N<T>
  3. 因此,即使r的類型爲R<?>,呼叫p(r.o)理論上應該是合法的。

這可能是Eclipse編譯器的推理(衆所周知的,使正確的津貼泛型其中javac doesn't的某些細微差別)。

假設你想用javac編譯,就像你提到的不能改變的v簽名,你能做的最好的是訴諸使用原始類型,泛型類型檢查的「選擇退出」:

public void v(final R<?> r) { 
    //necessary to placate javac - this is okay because [insert above reasoning] 
    @SuppressWarnings("rawtypes") 
    N nRaw = r.o; 
    p(nRaw); 
} 
相關問題