2013-05-22 35 views
3

我遇到了一個例子,表明擦除在方法簽名和方法上做了不同的處理,但我不知道爲什麼/如何。所述JLS §8.4.8.3狀態:方法簽名和方法擦除做的不同嗎?

它是一個編譯時間錯誤,如果一個類型聲明T具有構件的方法m1和存在或T中聲明的方法平方米T使得的超類型,所有以下的條件成立:

  1. m1和m2具有相同的名稱。
  2. m2可從T.訪問
  3. m1的簽名不是m2的簽名的子簽名(第8.4.2節)。
  4. m1或某些方法m1覆蓋(直接或間接)的簽名與m2的簽名或某些方法m2覆蓋(直接或間接)具有相同的擦除。

的編譯時間錯誤example given

class C<T> { 
    T id (T x) {...} 
} 
class D extends C<String> { 
    Object id(Object x) {...} 
} 

的解釋:

這是因爲D.id(對象)非法是d,C.id的成員(String)被聲明爲超類型D,並且:

  • 兩種方法ODS具有相同的名稱,ID
  • C.id(字符串)是d訪問
  • D.id(對象)的簽名是不是那種C.id的子簽名(字符串)
  • 兩個方法具有相同的擦除

前兩點是顯而易見的,但我不明白的解釋的最後兩分。如果第三點持有,兩種方法如何擦除相同的擦除?從第三點看來,似乎使用參數化類C <字符串>的方法(即,id(字符串)而不是id(T))完成了簽名的擦除。如果是這種情況,那麼這兩種方法應該有不同的擦除,但是這個例子表明方法擦除是在非參數化類上完成的。那麼,擦除實際上如何應用於方法簽名和方法?

回答

1

擦除意味着通用類型T(在C的情況下爲String)的所有發生由Object(+所需類型鑄件)代替。由於類型信息以這種方式丟失,所以在示例中存在衝突 - JVM將無法決定調用哪種方法。

編輯(這是錯誤的)AFAIK:一個子簽名將是接受一個兼容型(例如一個超String類型)和/或返回一個協變型的方法。

我試了一下,它很混亂,但來到這樣的解釋:編譯器不擦除,而是替換通用簽名。因此,當D extends C<String>重寫C<String>.id的簽名變爲:String id(String x)。很顯然D的方法id有不一樣的簽名,也不是相同的擦除(因爲String不是一般類型)。因此D.id的簽名不是C的子簽名。 (與規則3相符)

另一方面,C<T>.id(T x)的刪除爲Object id(Object x),與刪除D.id(Object x)的刪除相同。 (與規則4匹配)

接下來,如果可以保持簽名和擦除對齊,則覆蓋id將是有效的。顯然這是可能的(雖然不是很有用):

class Foo<T> { 
    public void set(final T thing) {} 
} 

class Bar<T> extends Foo<T> { 
    @Override 
    public void set(final Object thing) {} 
} 

這個編譯在eclipse中沒有警告。