【譯】java8之接口的默認靜態(tài)方法

眾所周知视卢,我們應該使用接口編程,接口使得在交互時不需要關注具體的實現(xiàn)細節(jié)瓤漏,從而保持程序的松散耦合腾夯。在API的設計中,設計簡約而清晰的接口非常重要蔬充。被稱作固定定律的接口分離定律,其中有一條就講到了應該設計更小的特定客戶端接口而不是一個通用目的的接口班利。良好的接口設計是讓應用程序和庫的API保持簡潔高效的關鍵饥漫。

如果你曾有過接口API設計的經(jīng)驗,那么有時候你會感覺到為API增加方法的必要罗标。但是庸队,如果API一旦發(fā)布了积蜻,幾乎無法在保證已接口類的代碼不變的情況下來增加新的方法。舉個例子彻消,假設你設計了一個簡單的API Calculator竿拆,里面有add、subtract宾尚、devide和multiply函數(shù)丙笋。我們可以如下申明。簡便起見煌贴,我們使用int類型御板。

public interface Calculator {

  int add(int first, int second);

  int subtract(int first, int second);

  int divide(int number, int divisor);

  int multiply(int first, int second);
}

為了實現(xiàn)Calculator這個接口,需要寫如下一個BasicCalculator類

public class BasicCalculator implements Calculator {

  @Override
  public int add(int first, int second) {
    return first + second;
  }

  @Override
  public int subtract(int first, int second) {
    return first - second;
  }

  @Override
  public int divide(int number, int divisor) {
    if (divisor == 0) {
      throw new IllegalArgumentException("divisor can't be           zero.");
    }
    return number / divisor;
  }

  @Override
  public int multiply(int first, int second) {
    return first * second;
  }
}

靜態(tài)工廠方法

乍一看牛郑,Calculator這個API還是非常簡單實用巩掺,其他開發(fā)者只需要創(chuàng)建一個BasicCalculator就可以使用這個API沮榜。比如下面這兩段代碼:

Calculator calculator = new BasicCalculator();
int sum = calculator.add(1, 2);

BasicCalculator cal = new BasicCalculator();
int difference = cal.subtract(3, 2);

然而,事實上給人的感覺卻是此API的用戶并不是面向這個接口進行編程,而是面向這個接口的實現(xiàn)類在編程冲呢。因為你的API并沒有強制要求用戶把BasicCalculator當著一個public類來對接口進行編程,如果你把BasicCalculator類變成protected類型你就需要提供一個靜態(tài)的工廠類來謹慎的提供Calculator的實現(xiàn)低矮,代碼如下:

class BasicCalculator implements Calculator {
  // rest remains same
}

接下來油宜,寫一個工廠類來提供Calculator的實例,代碼如下:

public abstract class CalculatorFactory {

  public static Calculator getInstance() {
  return new BasicCalculator();
  }
}

這樣者甲,用戶就被強制要求對Calculator接口進行編程春感,并且不需要關注接口的詳細實現(xiàn)。

盡管我們通過上面的方式達到了我們想要的效果虏缸,但是我們卻因增加了一個新的CalculaorFactory類而增加了API的表現(xiàn)層次鲫懒。如果其他開發(fā)者要有效的利用這個Calculator API,就必須要先學會一個新的類的使用刽辙。但在java8之前窥岩,這是唯一的實現(xiàn)方式。

java8允許用戶在接口中定義靜態(tài)的方法宰缤,允許API的設計者在接口的內(nèi)部定義類似getInstance的靜態(tài)工具方法颂翼,從而保持API簡潔。靜態(tài)的接口方法可以用來替代我們平時專為某一類型寫的helper類慨灭。比如朦乏,Collections類是用來為Collection和相關接口提供各種helper方法的一個類。Collections類中定義的方法應該直接添加到Collection及其它相關子接口上氧骤。

下面的代碼就是Java8中直接在Calculator接口中添加靜態(tài)getInstance方法的例證:

public interface Calculator {

  static Calculator getInstance() {
  return new BasicCalculator();
  }

  int add(int first, int second);

  int subtract(int first, int second);

  int divide(int number, int divisor);

  int multiply(int first, int second);

}

隨著時間來演進API

有些用戶打算通過新建一個Calculator子接口并在子接口中添加新方法或是通過重寫一個繼承自Calculator接口的實現(xiàn)類來增加類似remainder的方法呻疹。與他們溝通之后你會發(fā)現(xiàn),他們可能僅僅是想向Calculator接口中添加一個類似remainder的方法筹陵。感覺這僅僅是一個非常簡單的API變化刽锤,所以你加入了一個方法镊尺,代碼如下:
public interface Calculator {

  static Calculator getInstance() {
    return new BasicCalculator();
  }

  int add(int first, int second);

  int subtract(int first, int second);

  int divide(int number, int divisor);

  int multiply(int first, int second);

  int remainder(int number, int divisor); // new method added to API
}

在接口中增加方法會破壞代碼的兼容性,這意味著那些實現(xiàn)Calculator接口的用戶需要為remainder方法添加實現(xiàn)并思,否則庐氮,代碼無法編譯通過。這對API設計者來說非常麻煩宋彼,因為它讓代碼演進變得非常困難弄砍。Java8之前,開發(fā)者不能直接在API中實現(xiàn)方法宙暇,這也使得在一個API中添加一個或者多個接口定義變得十分困難输枯。

為了讓API的演進變得更加容易,Java8允許用戶對定義在接口中的方法提供默認的實現(xiàn)占贫。這就是通常的default或者defender方法桃熄。接口的實現(xiàn)類不需要再提供接口的實現(xiàn)方法。如果接口的實現(xiàn)類提供了方法型奥,那么實現(xiàn)類的方法會被調(diào)用瞳收,否則接口中的方法會被調(diào)用。List接口有幾個定義在接口中的默認方法厢汹,比如replaceAll,sort和splitIterator螟深。

default void replaceAll(UnaryOperator<E> operator) {
  Objects.requireNonNull(operator);
  final ListIterator<E> li = this.listIterator();
  while (li.hasNext()) {
    li.set(operator.apply(li.next()));
  }
}

我們可以向下面的代碼一樣定義一個默認的方法來解決API的問題。默認方法通常用在使用那些已經(jīng)存在的方法烫葬,比如remainder是用來定義使用subtract,multiply和divice的一個方法界弧。

default int remainder(int number, int divisor) {
  return subtract(number, multiply(divisor, divide(number, divisor)));
}

多重繼承

一個類只能繼承自一個父類,但可以實現(xiàn)多個接口搭综。既然在接口中實現(xiàn)方法是可行的垢箕,接口方法的多實現(xiàn)也是可行的。之前兑巾,Java已經(jīng)在類型上支持多重繼承条获,如今也支持在表現(xiàn)階段的多重繼承。下面這三條規(guī)則決定了哪個方法會被調(diào)用蒋歌。

規(guī)則一:定義在類中的方法優(yōu)先于定義在接口的方法:

interface A {
  default void doSth(){
  System.out.println("inside A");
  }
}
class App implements A{

  @Override
  public void doSth() {
    System.out.println("inside App");
  }

  public static void main(String[] args) {
    new App().doSth();
  }
}

運行結果:inside App帅掘。調(diào)用的是在類中定義的方法。

規(guī)則二:否則堂油,調(diào)用定制最深的接口中的方法修档。

interface A {
  default void doSth() {
    System.out.println("inside A");
  }
}
interface B {}
interface C extends A {
  default void doSth() {
    System.out.println("inside C");
  } 
}
class App implements C, B, A {

  public static void main(String[] args) {
    new App().doSth();
  }
}

運行結果:inside C

規(guī)則三:否則,直接調(diào)用指定接口的實現(xiàn)方法

interface A {
  default void doSth() {
    System.out.println("inside A");
  }
}
interface B {
  default void doSth() {
    System.out.println("inside B");
  }
}
class App implements B, A {

  @Override
  public void doSth() {
    B.super.doSth();
  }

  public static void main(String[] args) {
      new App().doSth();
  }
}

運行結果: inside B

水平有限府框,歡迎大家指點和建議萍悴,-

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市寓免,隨后出現(xiàn)的幾起案子癣诱,更是在濱河造成了極大的恐慌,老刑警劉巖袜香,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撕予,死亡現(xiàn)場離奇詭異,居然都是意外死亡蜈首,警方通過查閱死者的電腦和手機实抡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來欢策,“玉大人吆寨,你說我怎么就攤上這事〔瓤埽” “怎么了啄清?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長俺孙。 經(jīng)常有香客問我辣卒,道長,這世上最難降的妖魔是什么睛榄? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任荣茫,我火速辦了婚禮,結果婚禮上场靴,老公的妹妹穿的比我還像新娘啡莉。我一直安慰自己,他們只是感情好旨剥,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布咧欣。 她就那樣靜靜地躺著,像睡著了一般泞边。 火紅的嫁衣襯著肌膚如雪该押。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天阵谚,我揣著相機與錄音蚕礼,去河邊找鬼。 笑死梢什,一個胖子當著我的面吹牛奠蹬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嗡午,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼囤躁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起狸演,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤言蛇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后宵距,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腊尚,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年满哪,在試婚紗的時候發(fā)現(xiàn)自己被綠了婿斥。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡哨鸭,死狀恐怖民宿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情像鸡,我是刑警寧澤活鹰,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站坟桅,受9級特大地震影響华望,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜仅乓,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一赖舟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧夸楣,春花似錦宾抓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至紧显,卻和暖如春讲衫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背孵班。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工涉兽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人篙程。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓枷畏,卻偏偏與公主長得像,于是被迫代替她去往敵國和親虱饿。 傳聞我的和親對象是個殘疾皇子拥诡,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,525評論 25 707
  • Streams 原文鏈接: Streams 原文作者: shekhargulati 譯者: leege100 狀態(tài)...
    忽來閱讀 5,516評論 3 32
  • 這兩天經(jīng)歷了一些事情 做了一些決定 補了考 退了部 也不再為那些瑣碎的事情所羈絆 曾經(jīng)也想過大學四年都不要補考 但...
    張空空閱讀 241評論 0 0
  • 《阿房宮賦》是晚唐著名詩人杜牧創(chuàng)作的一篇散文触趴,遣詞用字無比華美,思想深刻見骨渴肉,是膾炙人口的經(jīng)典古文之一冗懦,下面我們一...
    wv橙子閱讀 618評論 0 1
  • 《理想國》解讀的第一期,將首先介紹《理想國》的創(chuàng)作背景宾娜,以及柏拉圖的政治實踐與這部作品的關系批狐;隨后進入這部作品本身...
    趙清炳閱讀 695評論 0 0