Java8默認方法DefaultMethods的原理及實例用法

這篇文章主要講解了“Java8默認方法DefaultMethods的原理及實例用法”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Java8默認方法DefaultMethods的原理及實例用法”吧!

創新互聯公司致力于網站制作、網站建設,成都網站設計,集團網站建設等服務標準化,推過標準化降低中小企業的建站的成本,并持續提升建站的定制化服務水平進行質量交付,讓企業網站從市場競爭中脫穎而出。 選擇創新互聯公司,就選擇了安全、穩定、美觀的網站建設服務!

Java 8 引入了新的語言特性——默認方法(Default Methods)。

Default methods enable new functionality to be added to the interfaces of libraries and ensure binary compatibility with code written for older versions of those interfaces.

默認方法允許您添加新的功能到現有庫的接口中,并能確保與采用舊版本接口編寫的代碼的二進制兼容性。

默認方法是在接口中的方法簽名前加上了 default 關鍵字的實現方法。

一個簡單的例子

interface InterfaceA {  default void foo() {    System.out.println("InterfaceA foo");  }}class ClassA implements InterfaceA {}public class Test {  public static void main(String[] args) {    new ClassA().foo(); // 打印:“InterfaceA foo”  }}

ClassA 類并沒有實現 InterfaceA 接口中的 foo 方法,InterfaceA 接口中提供了 foo 方法的默認實現,因此可以直接調用 ClassA 類的 foo 方法。

為什么要有默認方法

在 java 8 之前,接口與其實現類之間的 耦合度 太高了(tightly coupled),當需要為一個接口添加方法時,所有的實現類都必須隨之修改。默認方法解決了這個問題,它可以為接口添加新的方法,而不會破壞已有的接口的實現。這在 lambda 表達式作為 java 8 語言的重要特性而出現之際,為升級舊接口且保持向后兼容(backward compatibility)提供了途徑。

String[] array = new String[] {    "hello",    ", ",    "world",};List<String> list = Arrays.asList(array);list.forEach(System.out::println); // 這是 jdk 1.8 新增的接口默認方法

這個 forEach 方法是 jdk 1.8 新增的接口默認方法,正是因為有了默認方法的引入,才不會因為 Iterable 接口中添加了 forEach 方法就需要修改所有 Iterable 接口的實現類。

下面的代碼展示了 jdk 1.8 的 Iterable 接口中的 forEach 默認方法:

package java.lang;import java.util.Objects;import java.util.function.Consumer;public interface Iterable<T> {  default void forEach(Consumer<? super T> action) {    Objects.requireNonNull(action);    for (T t : this) {      action.accept(t);    }  }}

默認方法的繼承

和其它方法一樣,接口默認方法也可以被繼承。

interface InterfaceA {  default void foo() {    System.out.println("InterfaceA foo");  }}interface InterfaceB extends InterfaceA {}interface InterfaceC extends InterfaceA {  @Override  default void foo() {    System.out.println("InterfaceC foo");  }}interface InterfaceD extends InterfaceA {  @Override  void foo();}public class Test {  public static void main(String[] args) {    new InterfaceB() {}.foo(); // 打印:“InterfaceA foo”    new InterfaceC() {}.foo(); // 打印:“InterfaceC foo”    new InterfaceD() {      @Override      public void foo() {        System.out.println("InterfaceD foo");      }    }.foo(); // 打印:“InterfaceD foo”        // 或者使用 lambda 表達式    ((InterfaceD) () -> System.out.println("InterfaceD foo")).foo();  }}

接口默認方法的繼承分三種情況(分別對應上面的 InterfaceB 接口、InterfaceC 接口和 InterfaceD 接口):

不覆寫默認方法,直接從父接口中獲取方法的默認實現。

覆寫默認方法,這跟類與類之間的覆寫規則相類似。

覆寫默認方法并將它重新聲明為抽象方法,這樣新接口的子類必須再次覆寫并實現這個抽象方法。

默認方法的多繼承

Java 使用的是單繼承、多實現的機制,為的是避免多繼承帶來的調用歧義的問題。當接口的子類同時擁有具有相同簽名的方法時,就需要考慮一種解決沖突的方案。

interface InterfaceA {  default void foo() {    System.out.println("InterfaceA foo");  }}interface InterfaceB {  default void bar() {    System.out.println("InterfaceB bar");  }}interface InterfaceC {  default void foo() {    System.out.println("InterfaceC foo");  }    default void bar() {    System.out.println("InterfaceC bar");  }}class ClassA implements InterfaceA, InterfaceB {}// 錯誤//class ClassB implements InterfaceB, InterfaceC {//}class ClassB implements InterfaceB, InterfaceC {  @Override  public void bar() {    InterfaceB.super.bar(); // 調用 InterfaceB 的 bar 方法    InterfaceC.super.bar(); // 調用 InterfaceC 的 bar 方法    System.out.println("ClassB bar"); // 做其他的事  }}

在 ClassA 類中,它實現的 InterfaceA 接口和 InterfaceB 接口中的方法不存在歧義,可以直接多實現。

在 ClassB 類中,它實現的 InterfaceB 接口和 InterfaceC 接口中都存在相同簽名的 foo 方法,需要手動解決沖突。覆寫存在歧義的方法,并可以使用 InterfaceName.super.methodName(); 的方式手動調用需要的接口默認方法。

接口繼承行為發生沖突時的解決規則

值得注意的是這么一種情況:

interface InterfaceA {  default void foo() {    System.out.println("InterfaceA foo");  }}interface InterfaceB extends InterfaceA {  @Override  default void foo() {    System.out.println("InterfaceB foo");  }}// 正確class ClassA implements InterfaceA, InterfaceB {}class ClassB implements InterfaceA, InterfaceB {  @Override  public void foo() {//    InterfaceA.super.foo(); // 錯誤    InterfaceB.super.foo();  }}

當 ClassA 類多實現 InterfaceA 接口和 InterfaceB 接口時,不會出現方法名歧義的錯誤。當 ClassB 類覆寫 foo 方法時,無法通過 InterfaceA.super.foo(); 調用 InterfaceA 接口的 foo 方法。

因為 InterfaceB 接口繼承了 InterfaceA 接口,那么 InterfaceB 接口一定包含了所有 InterfaceA 接口中的字段方法,因此一個同時實現了 InterfaceA 接口和 InterfaceB 接口的類與一個只實現了 InterfaceB 接口的類完全等價。

這很好理解,就相當于 class SimpleDateFormat extends DateFormat 與 class SimpleDateFormat extends DateFormat, Object 等價(如果允許多繼承)。

或者換種方式理解:

class ClassC {  public void foo() {    System.out.println("ClassC foo");  }}class ClassD extends ClassC {  @Override  public void foo() {    System.out.println("ClassD foo");  }}public class Test {  public static void main(String[] args) {    ClassC classC = new ClassD();    classC.foo(); // 打印:“ClassD foo”  }}

這里的 classC.foo(); 同樣調用的是 ClassD 類中的 foo 方法,打印結果為“ClassD foo”,因為 ClassC 類中的 foo 方法在 ClassD 類中被覆寫了。

在上面的 ClassA 類中不會出現方法名歧義的原因是所謂“存在歧義”的方法其實都來自于 InterfaceA 接口,InterfaceB 接口中的“同名方法”只是繼承自 InterfaceA 接口而來并對其進行了覆寫。ClassA 類實現的兩個接口不是兩個毫不相干的接口,因此不存在同名歧義方法。

而覆寫意味著對父類方法的屏蔽,這也是 Override 的設計意圖之一。因此在實現了 InterfaceB 接口的類中無法訪問已被覆寫的 InterfaceA 接口中的 foo 方法。

這是當接口繼承行為發生沖突時的規則之一,即 被其它類型所覆蓋的方法會被忽略。

如果想要調用 InterfaceA 接口中的 foo 方法,只能通過自定義一個新的接口同樣繼承 InterfaceA 接口并顯示地覆寫 foo 方法,在方法中使用 InterfaceA.super.foo(); 調用 InterfaceA 接口的 foo 方法,最后讓實現類同時實現 InterfaceB 接口和自定義的新接口,代碼如下:

interface InterfaceA {  default void foo() {    System.out.println("InterfaceA foo");  }}interface InterfaceB extends InterfaceA {  @Override  default void foo() {    System.out.println("InterfaceB foo");  }}interface InterfaceC extends InterfaceA {  @Override  default void foo() {    InterfaceA.super.foo();  }}class ClassA implements InterfaceB, InterfaceC {  @Override  public void foo() {    InterfaceB.super.foo();    InterfaceC.super.foo();  }}

注意! 雖然 InterfaceC 接口的 foo 方法只是調用了一下父接口的默認實現方法,但是這個覆寫 不能省略,否則 InterfaceC 接口中繼承自 InterfaceA 接口的隱式的 foo 方法同樣會被認為是被 InterfaceB 接口覆寫了而被屏蔽,會導致調用 InterfaceC.super.foo() 時出錯。

通過這個例子,應該注意到在使用一個默認方法前,一定要考慮它是否真的需要。因為 默認方法會帶給程序歧義,并且在復雜的繼承體系中容易產生編譯錯誤。濫用默認方法可能給代碼帶來意想不到、莫名其妙的錯誤。

接口與抽象類

當接口繼承行為發生沖突時的另一個規則是,類的方法聲明優先于接口默認方法,無論該方法是具體的還是抽象的。

interface InterfaceA {  default void foo() {    System.out.println("InterfaceA foo");  }  default void bar() {    System.out.println("InterfaceA bar");  }}abstract class AbstractClassA {  public abstract void foo();  public void bar() {    System.out.println("AbstractClassA bar");  }}class ClassA extends AbstractClassA implements InterfaceA {  @Override  public void foo() {    InterfaceA.super.foo();  }}public class Test {  public static void main(String[] args) {    ClassA classA = new ClassA();    classA.foo(); // 打印:“InterfaceA foo”    classA.bar(); // 打印:“AbstractClassA bar”  }}

ClassA 類中并不需要手動覆寫 bar 方法,因為優先考慮到 ClassA 類繼承了的 AbstractClassA 抽象類中存在對 bar 方法的實現,同樣的因為 AbstractClassA 抽象類中的 foo 方法是抽象的,所以在 ClassA 類中必須實現 foo 方法。

雖然 Java 8 的接口的默認方法就像抽象類,能提供方法的實現,但是他們倆仍然是 不可相互代替的:

接口可以被類多實現(被其他接口多繼承),抽象類只能被單繼承。  接口中沒有 this 指針,沒有構造函數,不能擁有實例字段(實例變量)或實例方法,無法保存 狀態(state),抽象方法中可以。  抽象類不能在 java 8 的 lambda 表達式中使用。  從設計理念上,接口反映的是 “like-a” 關系,抽象類反映的是 “is-a” 關系。

接口靜態方法

除了默認方法,Java 8 還在允許在接口中定義靜態方法。

interface InterfaceA {  default void foo() {    printHelloWorld();  }    static void printHelloWorld() {    System.out.println("hello, world");  }}public class Test {  public static void main(String[] args) {    InterfaceA.printHelloWorld(); // 打印:“hello, world”  }}

其他注意點

default 關鍵字只能在接口中使用(以及用在 switch 語句的 default 分支),不能用在抽象類中。  接口默認方法不能覆寫 Object 類的 equals、hashCode 和 toString 方法。  接口中的靜態方法必須是 public 的,public 修飾符可以省略,static 修飾符不能省略。  即使使用了 java 8 的環境,一些 IDE 仍然可能在一些代碼的實時編譯提示時出現異常的提示(例如無法發現 java 8 的語法錯誤),因此不要過度依賴 IDE。

感謝各位的閱讀,以上就是“Java8默認方法DefaultMethods的原理及實例用法”的內容了,經過本文的學習后,相信大家對Java8默認方法DefaultMethods的原理及實例用法這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創新互聯,小編將為大家推送更多相關知識點的文章,歡迎關注!

本文題目:Java8默認方法DefaultMethods的原理及實例用法
轉載來于:http://m.kartarina.com/article34/jedppe.html

成都網站建設公司_創新互聯,為您提供網站制作小程序開發移動網站建設網站設計網站維護自適應網站

廣告

聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯

商城網站建設
主站蜘蛛池模板: 久久精品成人无码观看56| 日本无码一区二区三区白峰美| 一本久道综合在线无码人妻| 亚洲熟妇无码爱v在线观看| 色欲AV永久无码精品无码 | 亚洲AV无码乱码在线观看性色扶| 无码人妻一区二区三区精品视频| 无码人妻AV一二区二区三区| 男人av无码天堂| 无码人妻精品一区二区三18禁| 亚洲综合无码无在线观看| 国产丝袜无码一区二区三区视频 | 色综合久久中文字幕无码| 亚洲一区二区无码偷拍| 亚洲日韩中文无码久久| 日韩aⅴ人妻无码一区二区| 亚洲中文字幕久久精品无码2021| 久久精品无码专区免费| 日韩人妻无码免费视频一区二区三区| 精品少妇无码AV无码专区| 人妻中文无码久热丝袜| 国产成人亚洲精品无码AV大片 | 中文字幕无码精品亚洲资源网久久| 亚洲av无码成人精品区在线播放| 中文字幕人成无码免费视频| 大桥久未无码吹潮在线观看| 无码综合天天久久综合网| 无码丰满熟妇一区二区| 亚洲日韩精品无码AV海量| 久久久久亚洲AV无码专区首JN | 本免费AV无码专区一区| 国产高清无码毛片| 免费无码又爽又刺激网站直播| 尤物永久免费AV无码网站 | 国产精品无码无卡无需播放器| 少妇特殊按摩高潮惨叫无码| 无码国产精品久久一区免费| 在线看片无码永久免费aⅴ| 人妻丰满熟妇A v无码区不卡| 免费无遮挡无码视频网站| 无码精品人妻一区二区三区影院|