2015-09-05 111 views
10

考慮以下兩類:爲什麼不能使用具有多態返回類型的基本類型?

public interface Foo<T> 
{ 
    public T moo(); 
} 

public class IntFoo implements Foo<Integer> 
{ 
    public int moo() 
    { 
     return 0; 
    } 
} 

該代碼將在publicintmoo產生一個錯誤,說int與重載方法的返回類型Integer不兼容。嚴格地說,這是真的,因爲int不是直接等於Integer。但是,我們都知道它們可以使用自動(非)裝箱隱式轉換爲對方。什麼是少知道的是,編譯器生成在本例中,電橋法的事實:

public class IntFoo implements Foo<Integer> 
{ 
    public <synthetic> <bridge> Object moo() 
    { 
     return this.moo(); // upcast 
    } 

    public Integer moo() { 
     return 0; 
    } 
} 

這也要做,因爲JVM解決方法時,返回類型之間的區別,並自Foo.moo擦除返回類型爲Object,編譯器生成一個與方法簽名相同的橋接方法。

我很奇怪,爲什麼這會不會與原始的多態返回類型以及工作:

public class IntFoo implements Foo<Integer> 
{ 
    public <synthetic> <bridge> Object moo() 
    { 
     return Integer.valueOf(this.moo()); 
    } 

    public int moo() 
    { 
     return 0; 
    } 
} 

似乎有沒有任何理由不具有這樣的特徵:

IntFoo intFoo = new IntFoo(); 
Foo<Integer> foo = intFoo; 
Integer i = foo.moo(); // calls the synthetic method, which boxes the result of the actual implementation 

實際上,REPL會話的這個屏幕截圖顯示我甚至能夠在我的custom programming language(編譯成Java字節碼)中實現這個功能:

REPL session

+0

很高興知道syntetic bridge方法,這對我來說是新的。 – Mifeet

回答

6

一如既往的這些問題,答案是你必須問語言設計師。我看不出有什麼理由不能做到這一點。但在我看來,這個功能將毫無意義。正如你在問題中指出的那樣,只有當moo在靜態類型IntFoo的變量上被調用纔會返回原語;對於Foo<Integer>類型的變量,Integer無論如何都會返回。所以你可以通過這樣做來達到基本相同的目的。

public class IntFoo implements Foo<Integer> { 

    @Override 
    public Integer moo() { return mooAsInt(); } 

    public int mooAsInt() { return 0; } 
} 

我個人認爲這是更好,因爲它更加明顯,當拳擊確實/不會發生。在您的建議中,moo()可能會返回intInteger,具體取決於變量的靜態類型,這會非常令人困惑。

+0

我不認爲這個功能在任何方面都毫無意義:一個具體的例子是擴展了Set '的BitSet類。這樣,您可以獲得BitSet的無框高性能,並在某些需要通用Set的情況下仍然使用Set 功能。 – Clashsoft

+0

@Clashsoft我完全同意這一點。我曾經爭辯過類似的東西(參見「SparseArray vs HashMap」),我認爲android的SparseArray 應該實現了Map 。這樣可以在需要時將其視爲「Map」,但僅處理原始鍵。這本來很好,但只要你可以通過重載方法來做到這一點 - 不需要新的規則。 –

+0

但是你只能重載參數類型,而不能返回類型。這使得在你的場景中覆蓋Map 是不可能的,這意味着你需要一個額外的包裝器來實現Map接口並委託給SpareArray實例。 – Clashsoft

2

原語的問題在於它們需要不同數量的空間在堆棧上......與所有在堆棧上佔用相同空間量的對象引用相反。並且你不能有一個可變大小的棧幀,因爲否則在運行時棧不知道該怎麼回去的方法退出

+2

我的例子中你需要不同的堆棧大小?如果直接調用重寫的方法,則會從實現中獲取「int」,否則橋接方法會被調用並處理您的裝箱。 – Clashsoft

3

在我的愚見,原因純粹是句法。正如您在問題中已經演示的那樣,可以生成返回int的方法和實現Foo接口方法返回Object字節碼的方法。

但是,如果你從來看(即不是字節碼)的Java語法點的問題,

public class IntFoo implements Foo<Integer> { 
    public int moo() { return 0; } 
} 

應該覆蓋方法Integer moo()它沒有。如您所說,Java區分返回類型,並且int moo()Integer moo()不同。

+0

確實,'int moo()'覆蓋'T moo()'('T = Integer')可能並不明顯。但如果你能寫出'Foo ',事情會更加明顯。猜猜我們必須等待Java 10然後:/ – Clashsoft

相關問題