Java 8默认方法可能会破坏您的(用户)代码
乍一看, 默認(rèn)方法為Java虛擬機(jī)的指令集帶來了一個很棒的新功能。 最后,庫開發(fā)人員能夠開發(fā)已建立的API,而不會對其用戶代碼造成不兼容性。 使用默認(rèn)方法,當(dāng)將新方法引入該接口時,任何實現(xiàn)庫接口的用戶類都會自動采用默認(rèn)代碼。 而且,一旦用戶更新了他的實現(xiàn)類,他就可以使用對他的特定用例更有意義的東西來覆蓋默認(rèn)值。 更好的是,用戶可以從重寫的方法中調(diào)用接口的默認(rèn)實現(xiàn),并在其周圍添加邏輯。
到目前為止,一切都很好。 但是,向已建立的接口添加默認(rèn)方法會使Java代碼無法編譯。 在查看示例時,這是最容易理解的。 讓我們假設(shè)一個庫需要其接口之一的類作為輸入:
interface SimpleInput {void foo();void bar(); }abstract class SimpleInputAdapter implements SimpleInput {@Overridepublic void bar() {// some default behavior ...} }在Java 8之前,接口和相應(yīng)適配器類的上述組合是Java編程語言中相當(dāng)普遍的模式。 圖書館供應(yīng)商通常會提供適配器,以節(jié)省圖書館用戶的鍵入時間。 但是,還額外提供了接口以允許多重繼承的近似。
讓我們進(jìn)一步假設(shè)用戶使用了此適配器:
class MyInput extends SimpleInputAdapter{@Overridepublic void foo() {// do something ...}@Overridepublic void bar() {super.bar();// do something additionally ...} }通過此實現(xiàn),用戶最終可以與庫進(jìn)行交互。 請注意,該實現(xiàn)如何覆蓋bar方法以向默認(rèn)實現(xiàn)添加一些功能。
那么,如果庫遷移到Java 8,會發(fā)生什么呢? 首先,該庫很可能會棄用適配器類,并將功能移至默認(rèn)方法。 結(jié)果,該接口現(xiàn)在將如下所示:
interface SimpleInput {void foo();default void bar() {// some default behavior} }使用此新界面,用戶可以更新其代碼以適應(yīng)默認(rèn)方法,而不必使用適配器類。 使用接口而不是適配器類的最大好處是能夠擴(kuò)展除特定適配器之外的另一個類。 讓我們付諸實踐,并遷移MyInput類以使用默認(rèn)方法。 因為我們現(xiàn)在可以擴(kuò)展另一個類,所以讓我們另外擴(kuò)展一些第三方基類。 這個基類的作用在這里并不特別相關(guān),因此讓我們假設(shè)這對我們的用例有意義。
class MyInput extends ThirdPartyBaseClass implements SimpleInput {@Overridepublic void foo() {// do something ...}@Overridepublic void bar() {SimpleInput.super.foo();// do something additionally ... } }為了實現(xiàn)與原始類中類似的行為,我們利用Java 8的新語法來調(diào)用特定接口的默認(rèn)方法。 另外,我們將myMethod的邏輯移到了一些基類MyBase 。 拍拍我們的肩膀。 很好的重構(gòu)!
我們正在使用的庫取得了巨大的成功。 但是,維護(hù)人員需要添加另一個接口以提供更多功能。 此接口表示一個CompexInput ,它使用其他方法擴(kuò)展了SimpleInput 。 因為默認(rèn)方法通常被認(rèn)為可以安全添加 ,所以維護(hù)者還重寫了SimpleInput的默認(rèn)方法,并添加了一些行為以提供更好的默認(rèn)值。 畢竟,在實現(xiàn)適配器類時,這樣做很普遍:
interface ComplexInput extends SimpleInput {void qux();@Overridedefault void bar() {SimpleInput.super.bar(); // so complex, we need to do more ...} }這項新功能非常強大,以至ThirdPartyBaseClass的維護(hù)者決定也依賴此庫。 為此,他為ThirdPartyLibrary實現(xiàn)了ComplexInput接口。
但這對MyInput類意味著什么? 由于通過擴(kuò)展ThirdPartyBaseClass隱式實現(xiàn)ComplexInput ,調(diào)用SimpleInput的默認(rèn)方法突然變得非法。 結(jié)果,用戶的代碼不再編譯。 而且,由于Java認(rèn)為此調(diào)用與調(diào)用間接超類的super-super方法一樣是非法的,因此現(xiàn)在通常也禁止調(diào)用此方法。 相反,您可以調(diào)用默認(rèn)方法
ComplexInput類。 但是,這需要您首先在MyInput顯式實現(xiàn)此接口。 對于圖書館的用戶來說,這種變化很有可能是出乎意料的!
奇怪的是,Java運行時沒有進(jìn)行這種區(qū)分。 JVM的驗證程序?qū)⒃试S已編譯的類調(diào)用SimpleInput::foo即使已加載的類在運行時通過擴(kuò)展ThirdPartyBaseClass的更新版本隱式實現(xiàn)了ComplexClass 。 這里只有編譯器抱怨。
但是我們從中學(xué)到什么呢? 簡而言之,請確保不要在另一個接口中覆蓋默認(rèn)方法。 既不使用其他默認(rèn)方法,也不使用抽象方法。 通常,要小心使用默認(rèn)方法。 它們可以像Java的collection接口一樣輕松地簡化已建立的API的演變,但它們本身卻很復(fù)雜,因為它們允許執(zhí)行類型層次結(jié)構(gòu)中的方法調(diào)用。 在Java 7之前,您只需要通過遍歷線性類層次結(jié)構(gòu)來查找實際調(diào)用的代碼。 僅當(dāng)您確實覺得有必要時才添加這種復(fù)雜性。
翻譯自: https://www.javacodegeeks.com/2014/05/java-8-default-methods-can-break-your-users-code.html
總結(jié)
以上是生活随笔為你收集整理的Java 8默认方法可能会破坏您的(用户)代码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 房屋备案需要什么手续和证件(房屋备案需要
- 下一篇: 安卓好玩单机游戏推荐(安卓好玩单机游戏)