亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java8默認(rèn)方法Default Methods原理及實(shí)例詳解

 更新時(shí)間:2020年01月03日 09:06:30   作者:Ebn Zhang  
這篇文章主要介紹了Java8默認(rèn)方法Default Methods原理及實(shí)例詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

這篇文章主要介紹了Java8默認(rèn)方法Default Methods原理及實(shí)例詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

Java 8 引入了新的語言特性——默認(rèn)方法(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.

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

默認(rèn)方法是在接口中的方法簽名前加上了 default 關(guān)鍵字的實(shí)現(xiàn)方法。

一個簡單的例子

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(); // 打?。骸癐nterfaceA foo”
  }
}

ClassA 類并沒有實(shí)現(xiàn) InterfaceA 接口中的 foo 方法,InterfaceA 接口中提供了 foo 方法的默認(rèn)實(shí)現(xiàn),因此可以直接調(diào)用 ClassA 類的 foo 方法。

為什么要有默認(rèn)方法

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

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

這個 forEach 方法是 jdk 1.8 新增的接口默認(rèn)方法,正是因?yàn)橛辛四J(rèn)方法的引入,才不會因?yàn)?Iterable 接口中添加了 forEach 方法就需要修改所有 Iterable 接口的實(shí)現(xiàn)類。

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

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);
    }
  }
}

默認(rèn)方法的繼承

和其它方法一樣,接口默認(rèn)方法也可以被繼承。

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(); // 打?。骸癐nterfaceA foo”
    new InterfaceC() {}.foo(); // 打?。骸癐nterfaceC foo”
    new InterfaceD() {
      @Override
      public void foo() {
        System.out.println("InterfaceD foo");
      }
    }.foo(); // 打印:“InterfaceD foo”
    
    // 或者使用 lambda 表達(dá)式
    ((InterfaceD) () -> System.out.println("InterfaceD foo")).foo();
  }
}

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

不覆寫默認(rèn)方法,直接從父接口中獲取方法的默認(rèn)實(shí)現(xiàn)。

覆寫默認(rèn)方法,這跟類與類之間的覆寫規(guī)則相類似。

覆寫默認(rèn)方法并將它重新聲明為抽象方法,這樣新接口的子類必須再次覆寫并實(shí)現(xiàn)這個抽象方法。

默認(rèn)方法的多繼承

Java 使用的是單繼承、多實(shí)現(xiàn)的機(jī)制,為的是避免多繼承帶來的調(diào)用歧義的問題。當(dāng)接口的子類同時(shí)擁有具有相同簽名的方法時(shí),就需要考慮一種解決沖突的方案。

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(); // 調(diào)用 InterfaceB 的 bar 方法
    InterfaceC.super.bar(); // 調(diào)用 InterfaceC 的 bar 方法
    System.out.println("ClassB bar"); // 做其他的事
  }
}

在 ClassA 類中,它實(shí)現(xiàn)的 InterfaceA 接口和 InterfaceB 接口中的方法不存在歧義,可以直接多實(shí)現(xiàn)。

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

接口繼承行為發(fā)生沖突時(shí)的解決規(guī)則

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

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();
  }
}

當(dāng) ClassA 類多實(shí)現(xiàn) InterfaceA 接口和 InterfaceB 接口時(shí),不會出現(xiàn)方法名歧義的錯誤。當(dāng) ClassB 類覆寫 foo 方法時(shí),無法通過 InterfaceA.super.foo(); 調(diào)用 InterfaceA 接口的 foo 方法。

因?yàn)?InterfaceB 接口繼承了 InterfaceA 接口,那么 InterfaceB 接口一定包含了所有 InterfaceA 接口中的字段方法,因此一個同時(shí)實(shí)現(xiàn)了 InterfaceA 接口和 InterfaceB 接口的類與一個只實(shí)現(xiàn)了 InterfaceB 接口的類完全等價(jià)。

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

或者換種方式理解:

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(); // 打?。骸癈lassD foo”
  }
}

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

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

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

這是當(dāng)接口繼承行為發(fā)生沖突時(shí)的規(guī)則之一,即 被其它類型所覆蓋的方法會被忽略。

如果想要調(diào)用 InterfaceA 接口中的 foo 方法,只能通過自定義一個新的接口同樣繼承 InterfaceA 接口并顯示地覆寫 foo 方法,在方法中使用 InterfaceA.super.foo(); 調(diào)用 InterfaceA 接口的 foo 方法,最后讓實(shí)現(xiàn)類同時(shí)實(shí)現(xiàn) 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 方法只是調(diào)用了一下父接口的默認(rèn)實(shí)現(xiàn)方法,但是這個覆寫 不能省略,否則 InterfaceC 接口中繼承自 InterfaceA 接口的隱式的 foo 方法同樣會被認(rèn)為是被 InterfaceB 接口覆寫了而被屏蔽,會導(dǎo)致調(diào)用 InterfaceC.super.foo() 時(shí)出錯。

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

接口與抽象類

當(dāng)接口繼承行為發(fā)生沖突時(shí)的另一個規(guī)則是,類的方法聲明優(yōu)先于接口默認(rèn)方法,無論該方法是具體的還是抽象的。

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(); // 打?。骸癐nterfaceA foo”
    classA.bar(); // 打印:“AbstractClassA bar”
  }
}

ClassA 類中并不需要手動覆寫 bar 方法,因?yàn)閮?yōu)先考慮到 ClassA 類繼承了的 AbstractClassA 抽象類中存在對 bar 方法的實(shí)現(xiàn),同樣的因?yàn)?AbstractClassA 抽象類中的 foo 方法是抽象的,所以在 ClassA 類中必須實(shí)現(xiàn) foo 方法。

雖然 Java 8 的接口的默認(rèn)方法就像抽象類,能提供方法的實(shí)現(xiàn),但是他們倆仍然是 不可相互代替的:

  • 接口可以被類多實(shí)現(xiàn)(被其他接口多繼承),抽象類只能被單繼承。
  • 接口中沒有 this 指針,沒有構(gòu)造函數(shù),不能擁有實(shí)例字段(實(shí)例變量)或?qū)嵗椒ǎ瑹o法保存 狀態(tài)(state),抽象方法中可以。
  • 抽象類不能在 java 8 的 lambda 表達(dá)式中使用。
  • 從設(shè)計(jì)理念上,接口反映的是 “l(fā)ike-a” 關(guān)系,抽象類反映的是 “is-a” 關(guān)系。

接口靜態(tài)方法

除了默認(rèn)方法,Java 8 還在允許在接口中定義靜態(tài)方法。

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”
  }
}

其他注意點(diǎn)

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

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java程序員容易犯的10大低級錯誤

    Java程序員容易犯的10大低級錯誤

    這篇文章主要介紹了Java程序員容易犯的10大低級錯誤,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-09-09
  • Spring Boot實(shí)現(xiàn)郵件發(fā)送必會的5種姿勢

    Spring Boot實(shí)現(xiàn)郵件發(fā)送必會的5種姿勢

    這篇文章主要給大家介紹了關(guān)于Spring Boot實(shí)現(xiàn)郵件發(fā)送必會的5種姿勢,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Spring Boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • spring cloud實(shí)現(xiàn)前端跨域問題的解決方案

    spring cloud實(shí)現(xiàn)前端跨域問題的解決方案

    這篇文章主要介紹了 spring cloud實(shí)現(xiàn)前端跨域問題的解決方案,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01
  • 詳解eclipse中Maven工程使用Tomcat7以上插件的方法

    詳解eclipse中Maven工程使用Tomcat7以上插件的方法

    本篇文章主要介紹了詳解eclipse中Maven工程使用Tomcat7以上插件的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12
  • Java數(shù)據(jù)類型超詳細(xì)示例講解

    Java數(shù)據(jù)類型超詳細(xì)示例講解

    Java程序中要求參與的計(jì)算的數(shù)據(jù),必須要保證數(shù)據(jù)類型的一致性,如果數(shù)據(jù)類型不一致將發(fā)生類型的轉(zhuǎn)換。本文將通過示例詳細(xì)說說Java中數(shù)據(jù)類型的轉(zhuǎn)換,感興趣的可以了解一下
    2022-11-11
  • SpringBoot+mail 輕松實(shí)現(xiàn)各類郵件自動推送

    SpringBoot+mail 輕松實(shí)現(xiàn)各類郵件自動推送

    在實(shí)際的項(xiàng)目開發(fā)過程中,經(jīng)常需要用到郵件通知功能,例如,通過郵箱注冊,郵箱找回密碼,郵箱推送報(bào)表等等,實(shí)際的應(yīng)用場景非常的多,今天通過這篇文章,我們一起來學(xué)習(xí)如何在 Spring Boot 中快速實(shí)現(xiàn)一個自動發(fā)送郵件的功能
    2024-07-07
  • Spring為singleton?bean注入prototype?bean

    Spring為singleton?bean注入prototype?bean

    這篇文章主要介紹了Spring為singleton?bean注入prototype?bean,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-07-07
  • JVM要雙親委派的原因及如何打破它

    JVM要雙親委派的原因及如何打破它

    平時(shí)做業(yè)務(wù)開發(fā)比較少接觸類加載器,但是如果想深入學(xué)習(xí),了解類加載的原理是必不可少的.java的類加載器有哪些?什么是雙親委派?為什么要雙親委派?如何打破它?接下來本文就帶大家詳細(xì)介紹這些知識 ,需要的朋友可以參考下
    2021-06-06
  • Java基礎(chǔ)類庫之StringBuffer類用法詳解

    Java基礎(chǔ)類庫之StringBuffer類用法詳解

    String類是在所有開發(fā)項(xiàng)目開發(fā)之中一定會使用的一個功能類。雖然String類很好用,但也有弊端——內(nèi)容不允許頻繁修改,所以為了解決問題,我們提供了StringBuffer類。本文就來講講StringBuffer類的用法
    2022-07-07
  • Spring中BeanFactory與FactoryBean接口的區(qū)別詳解

    Spring中BeanFactory與FactoryBean接口的區(qū)別詳解

    這篇文章主要給大家介紹了關(guān)于Spring中BeanFactory與FactoryBean接口的區(qū)別的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者使用Spring具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03

最新評論