欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

解析Java中的默認(rèn)方法

 更新時(shí)間:2015年07月10日 11:58:20   投稿:goldensun  
這篇文章主要介紹了Java中的默認(rèn)方法,包括繼承和調(diào)用等Java入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下

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

Java 8 就要來(lái)臨,盡管發(fā)布期限已經(jīng)被推遲, 我們?nèi)苑浅4_信在它最終發(fā)布的時(shí)候會(huì)支持lambdas 表達(dá)式。 前面提到過(guò),我們之前關(guān)于這個(gè)主題已經(jīng)討論了不少,不過(guò),lambdas表達(dá)式并不是Java 8中唯一改變的游戲規(guī)則。


假設(shè)Java 8 已經(jīng)發(fā)布并且包含了lambda?,F(xiàn)在你打算用一下lambda,最明顯的應(yīng)用場(chǎng)景莫過(guò)于對(duì)collection的每一個(gè)元素應(yīng)用lambda。
 

List<?> list = …
list.forEach(…); // 這就是lambda代碼

在java.util.List或者java.util.Collection接口里都找不到forEach的定義。通常能想到的解決辦法是在JDK里給相關(guān)的接口添加新的方法及實(shí)現(xiàn)。然而,對(duì)于已經(jīng)發(fā)布的版本,是沒(méi)法在給接口添加新方法的同時(shí)不影響已有的實(shí)現(xiàn)。

因此,如果在Java 8里使用lambda的時(shí)候,因?yàn)橄蚯凹嫒莸脑蚨荒苡糜赾ollection庫(kù),那有多糟糕啊。


由于上述原因,引入了一個(gè)新的概念。虛擬擴(kuò)展方法,也即通常說(shuō)的defender方法, 現(xiàn)在可以將其加入到接口,這樣可以提供聲明的行為的默認(rèn)實(shí)現(xiàn)。

簡(jiǎn)單的說(shuō),Java的接口現(xiàn)在可以實(shí)現(xiàn)方法了。默認(rèn)方法帶來(lái)的好處是可以為接口添加新的默認(rèn)方法,而不會(huì)破壞接口的實(shí)現(xiàn)。

在我看來(lái),這并非那種每天都會(huì)用到的Java特性,但是它絕對(duì)能讓Java的Collections API可以很自然的使用lambda。

最簡(jiǎn)單的例子

讓我們看一個(gè)最簡(jiǎn)單的例子:一個(gè)接口A,Clazz類實(shí)現(xiàn)了接口A。
 

public interface A {
  default void foo(){
    System.out.println("Calling A.foo()");
  }
}
 
public class Clazz implements A {
}

代碼是可以編譯的,即使Clazz類并沒(méi)有實(shí)現(xiàn)foo()方法。在接口A中提供了foo()方法的默認(rèn)實(shí)現(xiàn)。

使用這個(gè)例子的客戶端代碼:
 

Clazz clazz = new Clazz();
clazz.foo(); // 調(diào)用A.foo()

多重繼承?

有一個(gè)常見(jiàn)的問(wèn)題:人們會(huì)問(wèn) 當(dāng)他們第一次聽(tīng)到關(guān)于默認(rèn)方法的新的特性時(shí) “如果一個(gè)類實(shí)現(xiàn)了兩個(gè)接口,并且兩個(gè)接口都用相同的簽名定義了默認(rèn)方法,這該怎么辦?”讓我們用先前的例子來(lái)展示這個(gè)解決方案:
 

public interface A {
  default void foo(){
    System.out.println("Calling A.foo()");
  }
}
 
public interface B {
  default void foo(){
    System.out.println("Calling B.foo()");
  }
}
 
public class Clazz implements A, B {
}

這段代碼不能編譯 有以下原因:

java:class Clazz 從types A到B給foo()繼承了不相關(guān)的默認(rèn)值

為了修復(fù)這個(gè),在Clazz里我們不得不手動(dòng)解決通過(guò)重寫(xiě)沖突的方法:
 

public class Clazz implements A, B {
  public void foo(){}
}

但是如果我們想從接口A中調(diào)用默認(rèn)實(shí)現(xiàn)方法foo(),而不是實(shí)現(xiàn)我們自己的方法,該怎么辦呢?這是有可能的,引用A中的foo(),如下所示:
 

public class Clazz implements A, B {
  public void foo(){
    A.super.foo();
  }
}

現(xiàn)在我不能十分確信我喜歡這個(gè)最終方案。也許它比在簽名里聲明默認(rèn)方法的實(shí)現(xiàn)更為簡(jiǎn)練,正如在默認(rèn)方法規(guī)范的第一手稿里所聲明的:
 

public class Clazz implements A, B {
  public void foo() default A.foo;
}

但是這確實(shí)更改了語(yǔ)法,難道不是嗎?它看起來(lái)更像一個(gè)接口的方法聲明而不是實(shí)現(xiàn)。假若接口A和接口B定義了許多相互沖突的默認(rèn)方法,而我愿意使用所有接口A的默認(rèn)方法解決沖突,那又如何呢?目前我不得不一個(gè)接著一個(gè)的解決沖突,改寫(xiě)每一對(duì)沖突的方法。這可能需要大量的工作和書(shū)寫(xiě)大量的模板代碼。

我估計(jì)解決沖突的方法需要進(jìn)行大量的討論,不過(guò)看起來(lái)創(chuàng)建者決定接受這無(wú)法避免的災(zāi)難。

真實(shí)的例子

默認(rèn)方法實(shí)現(xiàn)的真實(shí)例子可以在 JDK8早期打的包中找到。回到集合的forEach方法的例子中, 我們可以發(fā)現(xiàn)在java.lang.Iterable接口中,它的默認(rèn)實(shí)現(xiàn)如下:
 

@FunctionalInterface
public interface Iterable<T> {
  Iterator<T> iterator();
 
  default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
      action.accept(t);
    }
  }
}

forEach 使用了一個(gè)java.util.function.Consumer功能接口類型的參數(shù),它使得我們可以傳入一個(gè)lambda表達(dá)式或者一個(gè)方法引用,如下:
 

List<?> list = …
list.forEach(System.out::println);

方法調(diào)用
讓我們看一下實(shí)際上是如何調(diào)用默認(rèn)的方法的。如果你不熟悉這個(gè)問(wèn)題,那么你可能有興趣閱讀一下Rebel實(shí)驗(yàn)室有關(guān)Java字節(jié)的報(bào)告。

從客戶端代碼的視角來(lái)看,默認(rèn)的方法僅僅是常見(jiàn)的虛擬方法。因此名字應(yīng)該是虛擬擴(kuò)展方法。因此對(duì)于把默認(rèn)方法實(shí)現(xiàn)為接口的簡(jiǎn)單例子類來(lái)說(shuō),客戶端代碼將在調(diào)用默認(rèn)方法的地方自動(dòng)調(diào)用接口。
 

A clazz = new Clazz();
clazz.foo(); // invokeinterface foo()
 
Clazz clazz = new Clazz();
clazz.foo(); // invokevirtual foo()

如果默認(rèn)方法的沖突已經(jīng)解決,那么當(dāng)我們修改默認(rèn)方法并指定調(diào)用其中一個(gè)接口時(shí)候,invokespecial將給我們指定具體調(diào)用哪個(gè)接口的實(shí)現(xiàn)。
 

public class Clazz implements A, B {
  public void foo(){
    A.super.foo(); // invokespecial foo()
  }
}

下面是javap的輸出:

public void foo();
Code:
0: aload_0
1: invokespecial #2 // InterfaceMethod A.foo:()V
4: return

正如你看到的:invokespecial指令用來(lái)調(diào)用接口方法foo()。從字節(jié)碼的視角來(lái)看,這仍是新鮮的事情,因?yàn)橐郧澳阒荒芡ㄟ^(guò)指向一個(gè)類(父類)的而不是指向一個(gè)接口的super來(lái)調(diào)用方法。

最后…

默認(rèn)方法是對(duì)Java語(yǔ)言的有趣補(bǔ)充 – 你可以把他們看做是lambdas表達(dá)式和JDK庫(kù)之間的橋梁。默認(rèn)表達(dá)式的主要目標(biāo)是使標(biāo)準(zhǔn)JDK接口得以進(jìn)化,并且當(dāng)我們最終開(kāi)始使用Java 8的lambdas表達(dá)式時(shí),提供給我們一個(gè)平滑的過(guò)渡體驗(yàn)。誰(shuí)知道呢,也許將來(lái)我們會(huì)在API設(shè)計(jì)中看到更多的默認(rèn)方法的應(yīng)用。

相關(guān)文章

  • springBoot+webMagic實(shí)現(xiàn)網(wǎng)站爬蟲(chóng)的實(shí)例代碼

    springBoot+webMagic實(shí)現(xiàn)網(wǎng)站爬蟲(chóng)的實(shí)例代碼

    這篇文章主要介紹了springBoot+webMagic實(shí)現(xiàn)網(wǎng)站爬蟲(chóng)的實(shí)例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • springboot + JPA 配置雙數(shù)據(jù)源實(shí)戰(zhàn)

    springboot + JPA 配置雙數(shù)據(jù)源實(shí)戰(zhàn)

    這篇文章主要介紹了springboot + JPA 配置雙數(shù)據(jù)源實(shí)戰(zhàn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 詳解Idea SpringBoot搭建SpringCloud的準(zhǔn)備工作(推薦)

    詳解Idea SpringBoot搭建SpringCloud的準(zhǔn)備工作(推薦)

    這篇文章主要介紹了Idea SpringBoot搭建SpringCloud的準(zhǔn)備工作(推薦),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Java雜談之如何消除代碼中一大串參數(shù)列表

    Java雜談之如何消除代碼中一大串參數(shù)列表

    參數(shù)列表和字面意思差不多,主要是表示該參數(shù)數(shù)量不是固定的,可能會(huì)有一個(gè)參數(shù),可能多個(gè),不管多少個(gè)參數(shù),都放到一個(gè)數(shù)組處理,這種參數(shù)叫可變參數(shù)。可變長(zhǎng)參數(shù)要放在最后一個(gè)參數(shù)位置處理,但是一個(gè)函數(shù)里面不能有倆種類型的可變參數(shù)
    2021-10-10
  • SpringBoot文件訪問(wèn)映射如何實(shí)現(xiàn)

    SpringBoot文件訪問(wèn)映射如何實(shí)現(xiàn)

    這篇文章主要介紹了SpringBoot文件訪問(wèn)映射如何實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • java實(shí)現(xiàn)在SSM下使用支付寶掃碼支付功能

    java實(shí)現(xiàn)在SSM下使用支付寶掃碼支付功能

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)在SSM下使用支付寶掃碼支付功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-02-02
  • 關(guān)于自定義過(guò)濾器獲取不到session問(wèn)題

    關(guān)于自定義過(guò)濾器獲取不到session問(wèn)題

    這篇文章主要介紹了關(guān)于自定義過(guò)濾器獲取不到session問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • SpringBoot2.0整合Redis自定義注入bean組件配置的實(shí)戰(zhàn)教程

    SpringBoot2.0整合Redis自定義注入bean組件配置的實(shí)戰(zhàn)教程

    這篇文章主要介紹了SpringBoot2.0整合Redis自定義注入bean組件配置,我們將基于SpringBoot2.0整合搭建的微服務(wù)項(xiàng)目為奠基,開(kāi)啟中間件Redis的實(shí)戰(zhàn)之路,需要的朋友可以參考下
    2023-06-06
  • 關(guān)于@Valid注解大全以及用法規(guī)范

    關(guān)于@Valid注解大全以及用法規(guī)范

    這篇文章主要介紹了關(guān)于@Valid注解大全以及用法規(guī)范,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Mybatis下的SQL注入漏洞原理及防護(hù)方法解析

    Mybatis下的SQL注入漏洞原理及防護(hù)方法解析

    SQL 注入是發(fā)生在 Web 程序中數(shù)據(jù)庫(kù)層的安全漏洞,是網(wǎng)站存在最多也是最簡(jiǎn)單的漏洞,在實(shí)際項(xiàng)目中,即使使用了 Mybatis 框架,但仍然有可能因?yàn)榫幋a人員安全意識(shí)不足而導(dǎo)致 SQL 注入問(wèn)題,這篇文章主要介紹了Mybatis下的SQL注入漏洞原理及防護(hù)方法?,需要的朋友可以參考下
    2022-11-11

最新評(píng)論