tp3 默认模块 默认方法_您需要了解的有关默认方法的所有信息
tp3 默認模塊 默認方法
因此,默認方法是……昨天的新聞,對不對? 是的,但是使用了一年之后,積累了很多事實,我想將這些事實收集在一個地方,供剛開始使用它們的開發人員使用。 甚至有經驗的人都可以找到他們不知道的一兩個細節。
如果有新發現,我將在將來擴展此職位。 因此,我要我的讀者(是的,你們兩個!)向我提供有關默認方法的每個小事實,您在這里找不到這些方法。 如果您有任何東西,請鳴叫 , 發郵件或發表評論。
總覽
我想我沒有給這篇文章一個有意義的敘述。 原因在于,從本質上講,這是一篇Wiki文章。 它涵蓋了默認方法的不同概念和細節,盡管它們與自然相關,但它們并不適合進行連續敘述。
但這也有好處! 您可以輕松地在帖子中跳來跳去,而不會大大降低您的閱讀體驗。 查看目錄,以全面了解所涵蓋的內容,并好奇地將您帶到何處。
默認方法
到現在為止,大多數開發人員已經使用,閱讀甚至實現了默認方法,因此我將為每個人省去語法的詳細介紹。 在介紹更廣泛的概念之前,我將花更多時間在它的細節上。
句法
默認方法的新語言功能歸結為接口現在可以聲明非抽象方法,即帶有主體的方法。
以下示例是JDK 8中Comparator.thenComparing(Comparator)( link )的修改版:
比較器中的默認方法
default Comparator<T> thenComparing(Comparator<? super T> other) {return (o1, o2) -> {int res = this.compare(o1, o2);return (res != 0) ? res : other.compare(o1, o2);}; }除了關鍵字default之外,這看起來像一個“常規”方法聲明。 將這樣的方法添加到接口而沒有編譯錯誤并提示方法調用解析策略是必要的。
現在,實現Comparator每個類都將包含thenComparing(Comparator)公共方法,而不必自己實現-可以這么說,它是免費的。
顯式調用默認方法
在下面的內容中,我們將看到一些人們可能想要從某個特定的超級接口顯式調用方法的默認實現的原因。 如果有需要,請按以下步驟進行:
明確調用默認實現
class StringComparator implements Comparator<String> {// ...@Overridepublic Comparator<String> thenComparing(Comparator<? super String> other) {log("Call to 'thenComparing'.");return Comparator.super.thenComparing(other);} }注意接口的名稱如何用于指定以下super ,否則將引用超類(在本例中為Object )。 從句法上講,這類似于可以從嵌套類訪問對外部類的引用 。
解決策略
因此,讓我們考慮一個使用默認方法實現接口的類型的實例。 如果調用存在默認實現的方法會怎樣? (請注意,方法由其簽名標識,該簽名由名稱和參數類型組成。)
規則1 :Brian Goetz – 2013年3月3日(格式化我的)
首先,這闡明了為什么將這些方法稱為默認方法以及為什么必須使用關鍵字default啟動它們:
這樣的實現是備用的,以防萬一一個類并且其超類都不考慮該方法,即不提供任何實現,也不將其聲明為抽象(請參見規則#1 )。 等效地,僅當該類沒有實現擴展X并聲明相同方法的接口Y時,才使用接口X的默認方法(默認或抽象;請參見規則2 )。
盡管這些規則很簡單,但是它們并不能阻止開發人員創建復雜的情況。 這篇文章提供了一個示例,其中分辨率的預測并不簡單,并提出了應謹慎使用此功能的說法。
解決策略包含一些有趣的細節……
解決沖突
規則#3 ,或者說它的缺失,意味著具體的類必須實現存在競爭默認實現的每個方法。 否則,編譯器將引發錯誤。 如果有競爭的實現之一是合適的,則方法主體可以顯式調用該方法 。
這也意味著向接口添加默認實現會導致編譯錯誤。 如果一個類A實現無關接口X和Y并且其已經存在于默認方法X被添加到Y ,類A將不再編譯。
如果未A , X和Y一起編譯,并且JVM偶然發現這種情況,會發生什么情況? 有趣的問題, 答案似乎還不清楚 。 看起來JVM將拋出IncompatibleClassChangeError。
重新提取方法
如果抽象類或接口A某個方法聲明為抽象,而某些超級接口X存在默認實現,則X的默認實現將被覆蓋。 因此,所有子類型A具體類都必須實現該方法。 這可以用作強制實施不合適的默認實現的有效工具。
整個JDK中都使用了此技術,例如在ConcurrentMap ( link )上,該方法重新抽象了Map ( link )提供默認實現的許多方法,因為它們不是線程安全的(搜索術語“不合適的默認值”)。
注意,具體類仍可以選擇顯式調用覆蓋的默認實現 。
“對象”上的覆蓋方法
接口不可能為Object的方法提供默認實現。 嘗試這樣做將導致編譯錯誤。 為什么?
首先,這將毫無用處。 由于每個類都繼承自Object ,因此規則1明確表示永遠不會調用這些方法。
但是那條規則不是自然法則,專家組本可以例外。 郵件中還包含規則, Brian Goetz給出了為什么沒有規則的許多原因 。 我最喜歡的一個(格式化我的):
從根本toString ,來自Object的方法(例如toString , equals和hashCode )都是關于對象的state的 。 但是接口沒有狀態。 類有狀態。 這些方法屬于擁有對象狀態的代碼-類。
修飾符
請注意,您不能在默認方法上使用很多修飾符:
- 可見性固定為公開(與其他界面方法一樣)
- 關鍵字synchronized禁止(如在抽象方法)
- 關鍵字final是被禁止的(與抽象方法一樣)
當然,需要這些功能,并且對它們的缺失進行了全面的解釋(例如,針對final和synchronized )。 參數總是相似的:這不是默認方法的目的 ,引入這些功能將導致更復雜且易于出錯的語言規則和/或代碼。
不過,您可以使用static ,這將減少對復數形式的實用程序類的需求 。
一點上下文
現在我們已經知道了如何使用默認方法,下面將這些知識放到上下文中。
由F_A在CC-BY 2.0下發布 。
接口演變
經常可以找到引入默認方法的專家組,他們指出他們的目標是允許“接口演變”:
默認方法 […]的目的是使接口在首次發布后能夠以兼容的方式進行開發。
Brian Goetz – 2013年9月
在使用默認方法之前,幾乎不可能(不包括某些組織模式;請參閱此概述 )在不破壞所有實現的情況下向接口添加方法。 盡管這對于控制這些實現的絕大多數軟件開發人員都無關緊要,但對于API設計人員而言,這是一個至關重要的問題。 Java始終保持安全,在發布接口后也從未更改過接口。
但是隨著引入lambda表達式,這變得難以忍受。 想象一下總是編寫Stream.of(myList).forEach(...)的集體痛苦,因為無法將forEach添加到List 。
因此,引入lambdas的專家組決定尋找一種方法,以在不破壞任何現有實現的情況下實現接口演化。 他們對這一目標的關注解釋了默認方法的特征 。
在小組認為有可能且不降低該主要用例的可用性的情況下,他們還啟用了使用默認方法來創建特征的方法,或者甚至是接近它們的方法。 盡管如此,他們還是因為沒有“一路走到”混合蛋白和特質而經常受到攻擊,對此經常重復回答:“是的,因為那不是我們的目標。”
實用工具類
JDK以及特別常見的輔助庫(例如Guava和Apache Commons)充滿了實用程序類。 它們的名稱通常是為其提供方法的接口的復數形式,例如Collections或Sets 。 它們存在的主要原因是那些實用程序方法在發布后不能添加到原始接口。 使用默認方法,這成為可能。
現在,所有將接口實例作為參數的靜態方法都可以轉換為接口上的默認方法。 例如,查看靜態Collections.sort(List) ( link ),從Java 8開始,它靜態地委派給新實例默認方法List.sort(Comparator) ( link )。 我的帖子中給出了另一個示例, 說明如何使用默認方法來改進裝飾器模式 。 其他不帶參數的實用程序方法(通常是生成器)現在可以成為接口上的靜態默認方法。
盡管可以在代碼庫中刪除所有與接口相關的實用程序類,但不建議這樣做。 界面的可用性和內聚性應該仍然是主要優先事項-不要在其中填充所有可想象的功能。 我的猜測是,只有將這些方法中最通用的方法移到接口上,而在一個(或多個?)實用工具類中可以保留更多晦澀的操作,這才有意義。 (或完全刪除它們 ,如果您喜歡的話。)
分類
在對新Javadoc標簽的爭論中,Brian Goetz弱分類了到目前為止已引入JDK的默認方法(格式化我的方法):
1.可選方法 :它遵守合同,因為合同顯然是薄弱的,但是任何關心刪除的類肯定會覆蓋它。
對于大多數實現來說,這種實現是完美的,但是如果某些類(例如ArrayList )的維護者有足夠的動力去做的話,可能會有做得更好的機會。 Map上的新方法(例如putIfAbsent )也在此存儲桶中。
Brian Goetz – 2013年1月31日
我將此分類稱為“弱”分類,因為它自然缺乏關于在何處放置方法的硬性規定。 但是,這并沒有使它無用。 恰恰相反,我認為這對交流它們有很大幫助,在閱讀或編寫默認方法時要牢記一件好事。
文獻資料
請注意,默認方法是引入新的(非正式)Javadoc標簽@apiNote , @implSpec和@implNote的主要原因 。 JDK經常使用它們,因此了解它們的含義很重要。 了解它們的一個好方法是閱讀我的上一篇文章 (平穩,對嗎?),其中詳細介紹了它們。
繼承與建立類
關于繼承的不同方面以及如何使用它來構建類經常在有關默認方法的討論中出現。 讓我們仔細看看它們,看看它們與新語言功能的關系。
多重繼承-什么?
通過繼承,一個類型可以假定另一個類型的特征。 存在三種特征:
- 由子類型一種類型的輸入時 ,即是另一種類型的
- 行為 ,即一種類型繼承方法,因此其行為與另一種類型相同
- state ,即一個類型繼承了定義另一個類型狀態的變量
由于類是其父類的子類型并繼承所有方法和變量,因此類繼承顯然涵蓋了所有這三個特征。 同時,一個類只能擴展另一個類,因此僅限于單一繼承。
接口是不同的:一個類型可以繼承許多接口,并成為每個接口的子類型。 因此,從第一天開始,Java就一直支持這種多重繼承。
但是在Java 8之前,實現類僅繼承了接口的類型。 是的,它還繼承了合同,但沒有繼承其實際實施,因此它必須提供自己的行為。 使用默認方法時,這種情況發生了變化,因此從Java的版本8開始,還支持行為的多重繼承。
Java仍然沒有提供明確的方法來繼承多種類型的狀態。 但是,使用惡意方法或虛擬字段模式可以通過默認方法實現類似的效果。 前者很危險,不應該使用,后者也有一些缺點(特別是在封裝方面),應格外小心。
默認方法vs Mixins和特征
在討論默認方法時,有時會將它們與mixins和traits進行比較。 本文無法詳細介紹這些內容,但將粗略介紹它們與具有默認方法的接口的區別。 (可以在StackOverflow上找到mixins和trait的有用比較。)
混合蛋白
Mixins允許繼承其類型,行為和狀態。 一個類型可以從多個mixin繼承,從而提供所有三個特征的多重繼承。 根據語言的不同,也許還可以在運行時將混合添加到單個實例。
由于具有默認方法的接口不允許繼承狀態,因此顯然它們不是混合。
特質
與mixin相似,特征允許類型(和實例)從多個特征繼承。 它們還繼承了它們的類型和行為,但是與混合混合不同,常規特征沒有定義自己的狀態。
這使得特征類似于使用默認方法的接口。 概念仍然不同,但是這些差異并非完全無關緊要。 將來我可能會再次談到這一點,并進行更詳細的比較,但是在那之前,我將為您提供一些想法:
- 如我們所見, 方法調用解析并不總是那么瑣碎,這會使快速使用默認方法的不同接口的交互成為復雜性的負擔。 性狀通常以一種或另一種方式緩解此問題。
- 特性允許Java不完全支持的某些操作。 請參閱有關特質的Wikipedia文章中的“選擇操作”后的項目符號列表。
- 論文“ Java 8中的面向特征的編程”探討了使用默認方法的面向特征的編程風格,并遇到了一些問題。
因此,盡管具有默認方法的接口沒有任何特征,但是相似性允許像以前那樣以有限的方式使用它們。 這與專家組的設計目標一致, 該目標試圖在不與原始目標沖突的情況下適應此用例,即界面發展和易用性。
默認方法與抽象類
現在,接口可以提供行為,它們可以進入抽象類的領域,很快就會出現問題,可以在給定的情況下使用。
語言差異
首先讓我們說明一下語言層面的一些差異:
盡管接口允許多重繼承,但是它們基本上在類構建的其他各個方面都達不到要求。 默認方法永遠不會是最終方法,無法同步,也不能覆蓋Object的方法。 它們始終是公共的,這嚴重限制了編寫簡短且可重用方法的能力。 此外,接口仍然無法定義字段,因此每個狀態更改都必須通過公共API進行。 為適應該用例而對API進行的更改通常會破壞封裝。
盡管如此,仍然存在一些用例,其中這些差異無關緊要,并且兩種方法在技術上都是可行的。
概念差異
然后是概念上的差異。 類定義什么是什么,而接口通常定義什么可以做 。
抽象類是完全特殊的東西。 有效的Java項目18全面解釋了為什么接口在定義具有多個子類型的類型時優于抽象類。 (而且甚至不考慮默認方法。)要點是:抽象類對于接口的骨架(即部分)實現有效,但如果沒有匹配的接口就不應存在。
因此,當有效地將抽象類簡化為低可見性的接口的基本實現時,默認方法是否也可以消除這種缺陷? 決定: 不! 實現接口幾乎總是需要某些或所有默認方法所缺少的類構建工具。 而且,如果某些界面沒有,那顯然是一種特殊情況,這不會使您誤入歧途。 (有關使用默認方法實現接口時可能發生的情況,請參見此早期文章 。)
更多連結
- Lambda狀態的最終版本(第10章介紹了默認方法)
- 官方教程
- 關于如何發展接口的官方教程
- JavaCodeGeeks教程
- DZone教程
反射
這篇文章應該都談過了一個需要了解的默認方法。 如果您不同意,請鳴叫 , 發郵件或發表評論。 批準和+1也可以接受。
翻譯自: https://www.javacodegeeks.com/2015/02/everything-you-need-to-know-about-default-methods.html
tp3 默認模塊 默認方法
總結
以上是生活随笔為你收集整理的tp3 默认模块 默认方法_您需要了解的有关默认方法的所有信息的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 分数用英语怎么说
- 下一篇: 怎么让魔兽的电脑开矿(魔兽世界挖矿在哪里