1. 程式人生 > >Java8新特性之介面的預設方法和靜態方法

Java8新特性之介面的預設方法和靜態方法

我們都知道應該面向介面程式設計。介面給定使用者應該使用的協議,而不用依賴該介面的具體實現細節。

因此,為了做到鬆耦合,設計出乾淨的介面成為API設計的要素之一。SOLID五大原則之一的要求我們設計有具體目的的小介面,而不是一個通用卻臃腫的介面。對你的類庫和應用來說,介面設計是能否得到乾淨而高效的API的關鍵。

這一節的程式碼在ch01包中

如果你曾經設計過API,隨著時間的推移,你可能覺得有必要在API中新增新的方法。一旦API已經發布,想要在不破壞已有實現的前提下對一個介面新增方法會變得非常困難。為了說清這一點,假設你正在構建一個簡單的能夠支援操作的計算器API。我們可以寫一個Calculator

的介面,如下所示。為了簡單起見,我們將數值型別設為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);
}

回到這個Caculator介面,你建立了一個BasicCaculator的實現類,如下所示。

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

靜態工廠方法

假設這個計算器API非常有用也容易上手。使用者只需要自己建立一個BasicCalculator的例項,就能夠使用該API。你可以看到如下的程式碼。

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

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

然而有些使用者沒有面向介面Caculator進行程式設計,相反,他們面向該介面的 實現類BasicCalculator進行程式設計。你的API沒有強制使用者面向介面程式設計,因為BasicCalculator是public的。如果你將BasicCalculator宣告為protected,那麼你需要建立一個能夠提供Calculator實現類的靜態工廠。我們通過優化程式碼來解決這個問題。

首先,我們將BasicCalculator宣告為protected以便使用者不能夠直接使用該類。

class BasicCalculator implements Calculator {
  // rest remains same
}

接著,我們編寫一個能夠給我提供Calculator例項的工廠類,如下所示。

public abstract class CalculatorFactory {

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

現在,使用者會被迫面向Calculator介面進行程式設計,而且他們不能夠知道該介面具體的實現細節。

雖然我們實現了我們的目的,但我們新增的新類CaculatorFactory也增加了API的複雜度。現在API的使用者在使用API之前需要多瞭解一個類。這是在該問題在Java8之前唯一的解決方案。

Java8允許在介面中定義靜態方法。這允許API設計者在介面中定義像getInstance一樣的靜態工具方法,這樣就能夠使得API簡潔而精練。在介面中的靜態方法能夠用來代替輔助類(像CalculatorFactory),通常我們建立這些輔助類來定義一些與特定型別相關的輔助方法。舉例來說,Collections類就是一個定義了眾多輔助方法來與集合和其相關介面進行協作的輔助類。在Collections中定義的方法能夠輕易的新增到Collection介面或者它的子介面中。

以上的程式碼在Java8中可以通過新增在Calculator介面中新增一個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

有些使用者可能決定通過新增像remainder這樣的方法,或者為Calculator介面給出他們自己的實現來擴充套件CalculatorAPI。通過與使用者的溝通後,你發現大多數使用者想要在Calculator介面中新增一個remainder方法。這看起來是一個非常簡單的API變動,所以你在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
}

在介面中新增方法破壞了API的原始碼相容性。這意味著實現了Calculator介面的使用者需要新增remainder方法的實現,否則他們的程式碼將不能通過編譯。這對於API開發者來說是一個大問題,這使得API很難進行改進。在Java8之前,介面中是不能有方法的具體實現的。這往往在API需要拓展的時候成為一個問題,比如在介面定義中新增一兩個方法。

為了使API隨著時間的推移能夠不斷改進,Java8允許使用者在介面中給方法提供預設實現。這些方法被稱為default方法,或者defender方法。實現了該介面的類不需要給這些方法提供實現。如果一個實現類為這些方法提供了實現,那麼新給出的實現將會被使用,否則介面中的預設實現將被使用。List介面定義了一些default方法,像replaceAllsortsplitIterator

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

如下面的程式碼所示,我們可以通過新增一個default方法來解決上述的API問題。default方法通常使用已經存在的方法進行定義,如remainder方法使用了subtractmultiplydivide方法。

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

多重繼承

Java中一個類只能繼承一個類,但可以實現多個介面。現在在介面中包含方法的實現是可行的,Java也就有了類似多重繼承的特性。Java在型別層面已經存在多重繼承特性,現在在行為層面也有了多重繼承特性。有三條規則來幫助決定哪一個方法會被選中。

規則1:在類中定義的方法勝過在介面中定義的方法

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,因為在實現類中已經覆蓋了介面中定義的方法。

規則2:明確的介面勝過上層介面

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

規則3:執行在類中明確指明的實現方式

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

相關推薦

Java8特性介面預設方法靜態方法

我們都知道應該面向介面程式設計。介面給定使用者應該使用的協議,而不用依賴該介面的具體實現細節。 因此,為了做到鬆耦合,設計出乾淨的介面成為API設計的要素之一。SOLID五大原則之一的要求我們設計有具體目的的小介面,而不是一個通用卻臃腫的介面。對你的類庫和

【小家java】java8特性---外部迭代內部迭代(對比效能差異)

相關閱讀 從一個案例開始:遍歷一個集合 外部迭代 最傳統的方法是用Iterator,當然還以用for i、增強for迴圈等等。這一類方法叫做外部迭代,意為顯式地進行迭代操作,即集合中的元素訪問是由一個處於集合外部的東西來控制的,在這裡控制著迴圈的東西就是

Java8 特性預設介面方法

  摘要: 從java8開始,介面不只是一個只能宣告方法的地方,我們還可以在宣告方法時,給方法一個預設的實現,我們稱之為預設介面方法,這樣所有實現該介面的子類都可以持有該方法的預設實現。   · 待定   一. 引入預設介面方法的背景   java8可以看做是java版本更新迭代過程中變化最大的一個

Java8特性介面中的靜態方法預設方法詳解

在此之前可以先看一下這個大佬寫的文章,裡面關於Java 8介面中的靜態方法和預設方法的新特性解釋的蠻好的 在這裡補充幾點其他需要注意的,也可能是你一直疑惑的(我之前就困惑了好久)~ 介面中的靜態方法

java8特性二:介面預設方法靜態方法

java8中允許在介面中有普通方法。方法要用default修飾比如 default String getpassword (St

Java8特性介面中的預設方法靜態方法,你都掌握了嗎?

## 寫在前面 > 在Java8之前的版本中,介面中只能宣告常量和抽象方法,介面的實現類中必須實現介面中所有的抽象方法。而在Java8中,介面中可以宣告預設方法和靜態方法,本文,我們就一起探討下介面中的預設方法和靜態方法。 ## 介面中的預設方法 Java 8中允許介面中包含具有具體實現的方法,該

JDK8特性 介面的變化--預設方法靜態方法

本文主要是簡單的說明下jdk8出現的一個新特性–介面的增強。此次變化中,介面中某些方法也可以有方法體了。jdk8對於介面的設計,除了保留之前的版本記本特性外,還提供了兩個新的功能。除了抽象方法外,還可以有預設方法(default方法)和靜態方法(static方法

Java8特性的日期時間API

java8 時間日期API 一. LocalDate、LocalTime、Instant、Duration以及Period 1.LocalDate只提供日期,不含當天時間信息LocalDate date = LocalDate.of(2018, 5, 03); int year = da

Java8特性:默認方法

java8 默認方法 一. 默認方法 1.默認方法的使用模式: -- 可選方法 類實現了接口,但是刻意地將一些方法留白。public interface Iterator<E> { boolean hasNext(); E next

【小家java】Java8特性---CompletableFuture的系統講解例項演示(使用CompletableFuture構建非同步應用)

相關閱讀 【小家java】java5新特性(簡述十大新特性) 重要一躍 【小家java】java6新特性(簡述十大新特性) 雞肋升級 【小家java】java7新特性(簡述八大新特性) 不溫不火 【小家java】java8新特性(簡述十大新特性) 飽受讚譽 【小家java】java9

Java8特性forEach+Lambda 表示式遍歷MapList

今天來說一下Java8中forEach的簡單使用。我們使用對比的方式來看應該會看得更加清楚,更能理解: ============Java8之前的方式========== Map<String, Integer> items = new HashMap

java8特性函式式介面

雖然剛釋出了java11版本,但是對於java8中新的特性我們仍需要掌握,畢竟大部分開發都是用的jdk8,以及java11中仍然保留著這些特性,接下來我就網上查詢資料做了一下總結,如果哪裡說的不對頭,還請各位大佬指出來,我會及時更正。 1.函式式介面定義: 那麼函式介面到

JAVA8特性方法引用與構造器引用

一:簡介        1:什麼是方法引用?                    若Lambda體中的功能,已經有方法提供了實現,可以使用方法引用。                    可以將方法引用理解為Lambda表示式的另一種表現形式。        2:方法

java8介面預設方法靜態方法

java8中對介面進行了擴充套件,允許我們在介面中定義具體方法,一種是預設方法,即在方法返回值前加“default”關鍵字,另一種是加“static”的靜態方法。 擴充套件帶來的好處 1.java擁有了類似多繼承的功能,雖然在物件關係中,繼承關係和實現關係還是有有所區別,但

樂字節-Java8特性Base64重復註解與類型註解

ref sin pre easy 處理 on() append 靈活 rfc 上一篇小樂給大家說了《樂字節-Java8新特性之Date API》,接下來小樂繼續給大家說一說Java8新特性之Base64和重復註解與類型註解。 一、Base64 在Java 8中,內

Java8特性二:方法引用

輸出結果 知識 public ava urn strong class rules ros   上一節介紹了Java8新特性中的Lambda表達式,本小節繼續講解Java8的新特性之二:方法引用。方法引用其實也離不開Lambda表達式。 1、方法引用的使用場景   我們

Java8特性方法引用

# 一 前言 日常開發中,經常使用到Lambda表示式,例如: ```java public static void main(String[] args) { List list = Arrays.asList(1, 5, 10, 4, 2); // 列印列表中的每一個數字 li

Java8特性Optional

.get value api new java代碼 optional padding 正常 line 空指針異常一直是困擾Java程序員的問題,也是我們必須要考慮的。當業務代碼中充滿了if else判斷null 的時候程序變得不再優雅,在Java8中提供了Optional類

Java8特性:接口的默認方法與接口的靜態方法

err 類比 urn 多態 class 數組保存 統一 body bstr 接口的定義 接口的作用是定義該類型的實例要具有的功能,也就是必須執行哪些工作,並且不需要關心這些工作是怎麽具體進行的。接口定義的方法沒有方法體,並且接口不允許定義實例變量。如果一個類實現了這個接口就

jdk1.8特性 : 接口中可以有普通方法(非靜態方法)靜態方法 , 顛覆了之前我的理解 : 接口中只能有共有常量抽象方法的概念,後面必須要加一句jdk1.71..7之前

@override 編譯 sys 接口 blank new style nts highlight 看到jdk某些接口中存在default方法,於是... http://shaomeng95.iteye.com/blog/998820 為什麽接口只能是公有常量? p