Java8 新特性(一)- 介面增強

weixin_33890499發表於2018-01-24

本章目錄

  • Part One:介面預設方法
  • Part Two:介面靜態方法

隨著16年Android N的釋出,Google宣佈Android正式開始支援Java8。不過一直有點懶,拖啊拖,直到Java9已經出了不少日子了,才開始學Java8新特性~老實說,有點慚愧。
另外,Lambda表示式和方法引用越來越被大家熟悉使用,尤其是Rx全家桶裡,再不好好學,程式碼都快看不懂了,順道的把Java8的所有新特性也都研究一遍吧。


Part One:介面預設方法

從Java8開始,在介面中可以新增一個或者多個使用default關鍵字修飾的非抽象方法,就是介面的預設方法。預設方法可以讓我們的軟體介面增加新的方法,並且保證老版本的相容性。
在Java8中,官方提供的API裡就使用了預設方法,但是我們在呼叫這些API的時候,並沒有感覺到跟以前的使用有什麼不同之處,這就是預設方法的作用。例如, Iterator介面:


6879662-50a50c079fb0d72a.png
Iterator介面.png
  1. 預設方法的簡單實現
    暫時不考慮一些額外的干擾因素,只是實現簡單的預設方法並不複雜,程式碼如下:
package cn.tmooc.tarena;

interface Person{
    void eat();
    
    void sleep();
    
    /**
     * 介面的預設方法,獲取人體的BMI指數
     * @param height    米
     * @param weight    公斤
     * @return
     */
    default float getBMI(float height, float weight) {
        return weight / (float)Math.pow(height, 2);
    }
}

class Student implements Person{

    @Override
    public void eat() {
        System.out.println("學生去食堂吃飯");
    }

    @Override
    public void sleep() {
        System.out.println("學生去宿舍睡覺");
    }
    
}

class Parent implements Person{

    @Override
    public void eat() {
        System.out.println("家長回家吃飯");
    }

    @Override
    public void sleep() {
        System.out.println("家長回家睡覺");
    }
    
}

public class InterfaceSample {
    public static void main(String[] args) {
        Person student = new Student();
        student.eat();
        student.sleep();
        //呼叫介面的預設方法
        float bmi = student.getBMI(1.83F, 100);
        System.out.println("student的BMI指數 = " + bmi);
        System.out.println("==============================");
        Person mom = new Parent();
        mom.eat();
        mom.sleep();
        System.out.println("mom的BMI指數 = " + mom.getBMI(1.60F, 50));
    }
}

其中,Person是一個人類介面,定義了兩個普通方法和一個預設方法。而它的子類Student和Parent都必須實現普通方法,預設方法自動繼承了,可直接呼叫。
最後的結果如下:


6879662-167b9e0e2921201a.png
介面預設方法結果.png

預設方法的修改(增刪改)都不會影響到已經存在的介面的編譯,但是使用之前一定要仔細考慮是不是真的需要使用預設方法,因為在層級很複雜的情況下很容易引起模糊不清甚至變異錯誤。

  1. 多重繼承的衝突原則
    由於一個方法可以從不同的介面引入,那麼衝突就不可避免的,最簡單的兩條是
    2.1 子介面的優先順序更高,即如果介面之間有繼承關係,子介面的預設方法會覆蓋父介面的預設方法;
    2.2 類中的方法優先順序最高, 即實現類中重寫了預設方法,那麼以實現類為主;
package cn.tmooc.tarena;

interface A {
    default void hello() {
        System.out.println("Hello from A!");
    }
    
    default void hi() {
        System.out.println("Hi from A!");
    }
}

interface B extends A {
    default void hello() {
        System.out.println("Hello from B!");
    }
    
    default void hi() {
        System.out.println("Hi from B!");
    }
}

public class InterfaceSample implements B, A {
    
    @Override
    public void hi() {
        System.out.println("Hi from InterfaceSample!");
    }

    public static void main(String[] args) {
        InterfaceSample sample = new InterfaceSample();
        sample.hello(); //子介面的優先順序更高,所以結果為Hello from B!
        sample.hi(); //類中的方法優先順序最高,所以結果為Hi from InterfaceSample!
    }
}

2.3 如果兩個同級的介面,具有相同的方法,那麼需要顯示指定到底呼叫哪個方法。例如,兩個同級的介面都有hello方法


6879662-35a4069226e38bfa.png
同級介面衝突.png

程式碼會報錯,解決方法就是讓類實現某一個介面的該方法,使其具有更高的優先順序即可。

  1. 預設方法不能重寫Object方法
    例如我們重寫了toString方法,會報以下錯誤:


    6879662-a52465f2ac7118f5.png
    預設方法重寫.png
  2. Java8中抽象類和介面的異同
    相同點:
    都是抽象型別的;
    都可以有具體的實現方法;
    都可以不需要實現類或者繼承者去實現所有方法;
    不同點:
    宣告時使用的關鍵字不同;
    抽象類不可以多重繼承,介面可以;
    抽象類和介面所反映出的設計理念不同。其實抽象類表示的是"is-a"關係,介面表示的是"like-a"關係;
    介面中定義的變數預設是public static final 型,且必須給其初值,所以實現類中不能重新定義,也不能改變其值;抽象類中的變數可以是任意型別的,其值可以在子類中重新定義,也可以重新賦值。

Part Two:介面靜態方法

Java8為介面新增靜態方法後,可以把常用的工具方法直接寫在介面上,可以更好地組織程式碼,更易閱讀和使用。
例如,在官方的Comparator介面裡就提供了一個靜態比較方法:


6879662-96160b0196a6cecc.png
Comparator.png

寫法上和預設方法沒啥區別,就是修飾符不太一樣,這裡不再贅述了就。

相關文章