中文字幕日韩精品一区二区免费_精品一区二区三区国产精品无卡在_国精品无码专区一区二区三区_国产αv三级中文在线

Java8默認(rèn)方法DefaultMethods的原理及實(shí)例用法-創(chuàng)新互聯(lián)

這篇文章主要講解了“Java8默認(rèn)方法DefaultMethods的原理及實(shí)例用法”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Java8默認(rèn)方法DefaultMethods的原理及實(shí)例用法”吧!

為興文等地區(qū)用戶(hù)提供了全套網(wǎng)頁(yè)設(shè)計(jì)制作服務(wù),及興文網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為成都做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)、興文網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專(zhuān)業(yè)、用心的態(tài)度為用戶(hù)提供真誠(chéng)的服務(wù)。我們深信只要達(dá)到每一位用戶(hù)的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!

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ù)的接口中,并能確保與采用舊版本接口編寫(xiě)的代碼的二進(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 類(lèi)并沒(méi)有實(shí)現(xiàn) InterfaceA 接口中的 foo 方法,InterfaceA 接口中提供了 foo 方法的默認(rèn)實(shí)現(xiàn),因此可以直接調(diào)用 ClassA 類(lèi)的 foo 方法。

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

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

String[] array = new String[] {    "hello",    ", ",    "world",};List<String> 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)類(lèi)。

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

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

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

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

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

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

Java 使用的是單繼承、多實(shí)現(xiàn)的機(jī)制,為的是避免多繼承帶來(lái)的調(diào)用歧義的問(wèn)題。當(dāng)接口的子類(lèi)同時(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 類(lèi)中,它實(shí)現(xiàn)的 InterfaceA 接口和 InterfaceB 接口中的方法不存在歧義,可以直接多實(shí)現(xiàn)。

在 ClassB 類(lèi)中,它實(shí)現(xiàn)的 InterfaceB 接口和 InterfaceC 接口中都存在相同簽名的 foo 方法,需要手動(dòng)解決沖突。覆寫(xiě)存在歧義的方法,并可以使用 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 類(lèi)多實(shí)現(xiàn) InterfaceA 接口和 InterfaceB 接口時(shí),不會(huì)出現(xiàn)方法名歧義的錯(cuò)誤。當(dāng) ClassB 類(lèi)覆寫(xiě) foo 方法時(shí),無(wú)法通過(guò) InterfaceA.super.foo(); 調(diào)用 InterfaceA 接口的 foo 方法。

因?yàn)?InterfaceB 接口繼承了 InterfaceA 接口,那么 InterfaceB 接口一定包含了所有 InterfaceA 接口中的字段方法,因此一個(gè)同時(shí)實(shí)現(xiàn)了 InterfaceA 接口和 InterfaceB 接口的類(lèi)與一個(gè)只實(shí)現(xiàn)了 InterfaceB 接口的類(lèi)完全等價(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(); // 打印:“ClassD foo”  }}

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

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

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

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

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

接口與抽象類(lèi)

當(dāng)接口繼承行為發(fā)生沖突時(shí)的另一個(gè)規(guī)則是,類(lèi)的方法聲明優(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 類(lèi)中并不需要手動(dòng)覆寫(xiě) bar 方法,因?yàn)閮?yōu)先考慮到 ClassA 類(lèi)繼承了的 AbstractClassA 抽象類(lèi)中存在對(duì) bar 方法的實(shí)現(xiàn),同樣的因?yàn)?AbstractClassA 抽象類(lèi)中的 foo 方法是抽象的,所以在 ClassA 類(lèi)中必須實(shí)現(xiàn) foo 方法。

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

接口可以被類(lèi)多實(shí)現(xiàn)(被其他接口多繼承),抽象類(lèi)只能被單繼承。  接口中沒(méi)有 this 指針,沒(méi)有構(gòu)造函數(shù),不能擁有實(shí)例字段(實(shí)例變量)或?qū)嵗椒?,無(wú)法保存 狀態(tài)(state),抽象方法中可以。  抽象類(lèi)不能在 java 8 的 lambda 表達(dá)式中使用。  從設(shè)計(jì)理念上,接口反映的是 “l(fā)ike-a” 關(guān)系,抽象類(lèi)反映的是 “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 語(yǔ)句的 default 分支),不能用在抽象類(lèi)中。  接口默認(rèn)方法不能覆寫(xiě) Object 類(lèi)的 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ò)度依賴(lài) IDE。

感謝各位的閱讀,以上就是“Java8默認(rèn)方法DefaultMethods的原理及實(shí)例用法”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Java8默認(rèn)方法DefaultMethods的原理及實(shí)例用法這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司,,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

新聞標(biāo)題:Java8默認(rèn)方法DefaultMethods的原理及實(shí)例用法-創(chuàng)新互聯(lián)
當(dāng)前路徑:http://m.rwnh.cn/article10/pgddo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站營(yíng)銷(xiāo)、小程序開(kāi)發(fā)、定制開(kāi)發(fā)、定制網(wǎng)站企業(yè)建站、動(dòng)態(tài)網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)
常宁市| 永仁县| 巴彦淖尔市| 依兰县| 临海市| 贵港市| 镇江市| 黔江区| 盖州市| 长宁区| 大理市| 慈利县| 衡阳市| 武安市| 万宁市| 宁阳县| 岗巴县| 陵川县| 吉木萨尔县| 墨竹工卡县| 寿光市| 安义县| 军事| 南宁市| 天峻县| 德化县| 天津市| 乌拉特前旗| 罗定市| 定南县| 英超| 祁阳县| 通海县| 镇巴县| 通山县| 新乐市| 班玛县| 万盛区| 河南省| 利辛县| 赤水市|