2017-02-11 94 views
7

我有以下類:正在返回類型通用相同的擦除二進制兼容?

public abstract Foo { 
    Foo() {} 

    public abstract Foo doSomething(); 

    public static Foo create() { 
    return new SomePrivateSubclassOfFoo(); 
    } 
} 

我想將其更改爲以下定義:

public abstract Foo<T extends Foo<T>> { 
    Foo() {} 

    public abstract T doSomething(); 

    public static Foo<?> create() { 
    return new SomePrivateSubclassOfFoo(); 
    } 
} 

這是改變二進制兼容的? 也就是說,將編譯的版本與新版本的舊版本一起編譯而不重新編譯?

我知道我需要更改SomePrivateSubclassOfFoo,這沒關係。我也知道這個改變會在編譯舊客戶端代碼時觸發原始類型的警告,這對我也是可以的。我只是想確保舊客戶端代碼不需要重新編譯。

根據我的理解,這應該是可以的,因爲T的刪除是Foo,因此字節碼中的doSomething的簽名與之前相同。如果我查看由javap -s打印的內部類型簽名,我確實看到了這一點(雖然在不使用-s時打印的「非內部」類型簽名確實不同)。 我也測試過這個,它對我有用。

但是,Java API Compliance Checker告訴我,這兩個版本不是二進制兼容的。

那麼什麼是正確的? JLS在這裏保證二進制兼容性,還是在測試中我只是幸運的? (爲什麼會發生這種情況?)

回答

4

嗯,你的代碼似乎不會破壞二進制兼容性。
我發現這些一些爬行後/讀取
http://docs.oracle.com/javase/specs/jls/se8/html/jls-13.html#jls-13.4.5 它說: -

添加或刪除類的類型參數不對,本身具有二進制兼容性沒有任何影響。
...
更改類的類型參數的第一個邊界可能會更改任何使用該類型參數的成員的擦除(第4.6節),這可能會影響二進制兼容性。這種邊界的改變類似於方法或構造函數的類型參數的第一個邊界的改變(§13.4.13)。

http://wiki.eclipse.org/Evolving_Java-based_APIs_2#Turning_non-generic_types_and_methods_into_generic_ones做了進一步明確: -

按照特別投緣故事,Java編譯器會將原始類型爲類型的擦除的參考。通過將類型參數添加到類型聲明中並明智地將類型變量的使用引入到其現有方法和字段的簽名中,現有類型可演變爲泛型類型。只要刪除看起來像在生成之前的相應聲明,該變化就與現有代碼二進制兼容。

所以你現在沒有問題,因爲這是你第一次生成這個類。

但是請記住作爲上述DOC還說: -

不過,也銘記,有如何的類型或方法已經是通用的,可以兼容發展受到嚴重製約關於它的類型參數(見上表)。因此,如果您打算對API進行基因化,請記住,您只有一次機會(發佈),才能正確使用。尤其是,如果將API簽名中的類型從原始類型「列表」更改爲「列表<?>」或「列表<對象>」,那麼您將被鎖定在該決定中。道義是,對現有API進行基因化是應該從整個API的角度考慮,而不是逐個方法或逐類地逐個考慮。

所以我認爲,它是第一次做這個改變的好,但你只有一次機會,所以充分利用它!