2016-07-14 92 views
4

在將代碼庫從Java 1.7遷移到1.8期間,我們收到錯誤消息「方法...不適用於參數「在幾個代碼位置上,都遵循泛型使用中的相同模式。Java 8泛型此方法不適用於Eclipse中的參數

我們目前在Windows 7上大多使用Eclipse Mars(4.5.2),但也可以用Neon(4.6)確認行爲。 Javac以及ecj與1.7合規性級別都可以編譯我們的代碼沒有錯誤。

這裏是一個最小的,完整的,並且可驗證例如:

public class ComplexInterfaceTest { 

    public static class Foo {} 

    public interface Bar { 
    void print(); 
    } 

    public static class SubFooBar extends Foo implements Bar { 
    public void print() { 
     System.out.println(this.getClass().getSimpleName()); 
    } 
    } 

    public static class FooBar<T extends Foo & Bar> { 
    public static <T extends Foo & Bar> FooBar<T> makeFooBar() { 
     return new FooBar<>(); 
    } 

    public void create(T value) { 
     value.print(); 
     return; 
    } 
    } 

    public static class Base<T extends Foo> {} 

    public static class Subclass extends Base<SubFooBar> { 
    public void doSomething(SubFooBar value) { 
//  FooBar.<SubFooBar>makeFooBar().create(value); 
     FooBar.makeFooBar().create(value); 
    } 
    } 

    public static void main(String[] args) { 
    new Subclass().doSomething(new SubFooBar()); 
    } 

} 

現在doSomething方法切換註釋掉的行會使代碼編譯,所以我們有一個解決方法。仍然錯誤消息似乎不正確,因爲類SubFooBar延伸Foo和執行Bar,所以它履行<T extends Foo & Bar>,這是<T extends Foo & Bar> FooBar<T> makeFooBar()所需的合同,所以實際上T IMO應綁定到SubFooBar

我搜索了類似的問題,發現這些: Differences in type inference JDK8 javac/Eclipse Luna? Type Inference Compiler Error In Eclipse with Java8 but not with Java7

這讓我覺得這可能是一個ecj錯誤。在這個過程中我也看了成Eclipse Bugzilla但找不到任何可比性,我看到了這些:

  • 430686驗證固定的 - 我的是不是
  • 440019有標量型做的事 - 我的不
  • 492838,448793與通配符做的事情 - 我的不

現在Eclipse Bugzilla討論都充滿了對ecj的內部工作,我並不總是可以遵循的細節。我的理解雖然是普遍的共識,Eclipse編譯器必須嚴格遵循JLS而不是javac(如果它是錯誤的),所以它不一定是ecj中的錯誤。如果它不是ecj錯誤,則編譯代碼必須是javac錯誤。

我感興趣的是 - 對於那些可以分析我的代碼片段的類型推斷過程 - 應該代碼編譯還是編碼出錯?

編輯

正如我答應後我的報告到Eclipse的Bugzilla結果:缺陷的ID爲#497905(斯蒂芬·赫爾曼已經張貼在接受的答案低於他的評論的鏈接),目前針對v4.7。

+1

我剛剛發佈了一個關於Eclipse Bugzilla的bug報告,會在這裏發佈他們的答案。 –

回答

2

在該方法

public void doSomething(SubFooBar value) { 
    FooBar.makeFooBar().create(value); 
} 

方法makeFooBar()的類型參數T將永遠不會被推斷爲SubFooBar。之後將要將SubFooBar的實例傳遞給create方法的事實不影響前面的調用表達式FooBar.makeFooBar()的類型。

這不會隨着Java 8的目標輸入而改變,因爲這個新特性不適用於鏈接方法調用的接收器表達式。

因此,在所有版本中,makeFooBar()調用的T的類型將是交集類型Foo & Bar,因此結果類型爲FooBar<Foo&Bar>。這也是Eclipse推斷的結果,即使編輯器中的工具提示可能顯示其他內容。

這意味着,你可以傳遞一個SubFooBar實例的create方法FooBar<Foo&Bar>.create(…)預計Foo&BarSubFooBar延長Foo和實施Bar是兼容的一個實例。

有可能證明Eclipse一樣,推斷出相同類型的所有其他的編譯器,如插入適當的類型轉換

public void doSomething(SubFooBar value) { 
    FooBar.makeFooBar().create((Foo&Bar)value); 
} 

使編譯器錯誤消失。所以這裏的問題不是類型推斷,而是這個Eclipse版本認爲SubFooBar不能分配給Foo & Bar

+2

是的,這也符合我的分析(https://bugs.eclipse.org/bugs/show_bug.cgi?id=497905#c1) –

+0

所以基本上T因爲應該是'Foo&Bar'而SubFooBar是'Foo&Bar'應該編譯。很高興知道,謝謝! –

+1

我已經讓它有一段時間沉入 - 我認爲沒有太多可以補充的東西:儘管我錯過了正確的理由,但重點是我的代碼似乎是正確的,所以這個問題將被定位爲Eclipse 4.7版。 –

相關問題