真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Java8默認(rèn)方法DefaultMethods原理是什么-創(chuàng)新互聯(lián)

這篇文章主要介紹了Java8默認(rèn)方法Default Methods原理是什么,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)!專注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、小程序定制開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了岑鞏免費(fèi)建站歡迎大家使用!

Java 8 引入了新的語(yǔ)言特性——默認(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)有庫(kù)的接口中,并能確保與采用舊版本接口編寫的代碼的二進(jìn)制兼容性。

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

一個(gè)簡(jiǎ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)需要為一個(gè)接口添加方法時(shí),所有的實(shí)現(xiàn)類都必須隨之修改。默認(rèn)方法解決了這個(gè)問題,它可以為接口添加新的方法,而不會(huì)破壞已有的接口的實(shí)現(xiàn)。這在 lambda 表達(dá)式作為 java 8 語(yǔ)言的重要特性而出現(xiàn)之際,為升級(jí)舊接口且保持向后兼容(backward compatibility)提供了途徑。

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

這個(gè) forEach 方法是 jdk 1.8 新增的接口默認(rèn)方法,正是因?yàn)橛辛四J(rèn)方法的引入,才不會(huì)因?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 {
  default void forEach(Consumer 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)方法的繼承分三種情況(分別對(duì)應(yīng)上面的 InterfaceB 接口、InterfaceC 接口和 InterfaceD 接口):

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

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

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

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

Java 使用的是單繼承、多實(shí)現(xiàn)的機(jī)制,為的是避免多繼承帶來(lái)的調(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 {
}

// 錯(cuò)誤
//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 方法,需要手動(dòng)解決沖突。覆寫存在歧義的方法,并可以使用 InterfaceName.super.methodName(); 的方式手動(dòng)調(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(); // 錯(cuò)誤
    InterfaceB.super.foo();
  }
}

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

因?yàn)?InterfaceB 接口繼承了 InterfaceA 接口,那么 InterfaceB 接口一定包含了所有 InterfaceA 接口中的字段方法,因此一個(gè)同時(shí)實(shí)現(xiàn)了 InterfaceA 接口和 InterfaceB 接口的類與一個(gè)只實(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 類中不會(huì)出現(xiàn)方法名歧義的原因是所謂“存在歧義”的方法其實(shí)都來(lái)自于 InterfaceA 接口,InterfaceB 接口中的“同名方法”只是繼承自 InterfaceA 接口而來(lái)并對(duì)其進(jìn)行了覆寫。ClassA 類實(shí)現(xiàn)的兩個(gè)接口不是兩個(gè)毫不相干的接口,因此不存在同名歧義方法。

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

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

如果想要調(diào)用 InterfaceA 接口中的 foo 方法,只能通過(guò)自定義一個(gè)新的接口同樣繼承 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)方法,但是這個(gè)覆寫 不能省略,否則 InterfaceC 接口中繼承自 InterfaceA 接口的隱式的 foo 方法同樣會(huì)被認(rèn)為是被 InterfaceB 接口覆寫了而被屏蔽,會(huì)導(dǎo)致調(diào)用 InterfaceC.super.foo() 時(shí)出錯(cuò)。

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

接口與抽象類

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

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(); // 打?。骸癆bstractClassA bar”
  }
}

ClassA 類中并不需要手動(dòng)覆寫 bar 方法,因?yàn)閮?yōu)先考慮到 ClassA 類繼承了的 AbstractClassA 抽象類中存在對(duì) 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ū)嵗椒?,無(wú)法保存 狀態(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(); // 打?。骸癶ello, world”
  }
}

其他注意點(diǎn)

  • default 關(guān)鍵字只能在接口中使用(以及用在 switch 語(yǔ)句的 default 分支),不能用在抽象類中。

  • 接口默認(rèn)方法不能覆寫 Object 類的 equals、hashCode 和 toString 方法。

  • 接口中的靜態(tài)方法必須是 public 的,public 修飾符可以省略,static 修飾符不能省略。

  • 即使使用了 java 8 的環(huán)境,一些 IDE 仍然可能在一些代碼的實(shí)時(shí)編譯提示時(shí)出現(xiàn)異常的提示(例如無(wú)法發(fā)現(xiàn) java 8 的語(yǔ)法錯(cuò)誤),因此不要過(guò)度依賴 IDE。

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“Java8默認(rèn)方法Default Methods原理是什么”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián)建站,關(guān)注創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)建站www.cdcxhl.com,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。


當(dāng)前名稱:Java8默認(rèn)方法DefaultMethods原理是什么-創(chuàng)新互聯(lián)
網(wǎng)頁(yè)URL:http://weahome.cn/article/depigh.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部