Java基礎-接口、lambda表達式

Java 8新增的lambda表達式毫無疑問是使人很是激動的,今後咱們能夠很是簡潔的定義和使用代碼塊而不是用繁瑣的匿名內部類來實現。而接口是lambda表達式的基礎,要理解lambda表達式就要先理解接口的概念。java

接口

Java中接口是對類行爲的抽象。彷佛繼承也能作到這件事,它們的區別在於Java中類只能有一個父類,而接口是能夠實現多個的。因此接口更傾向於類的一部分抽象,也就是行爲的抽象,而不是類自己的抽象。編程

語法

要定義一個接口很簡單,使用關鍵字interface後面再跟上接口名稱就能夠了。類能夠用implements關鍵字來實現接口。安全

public interface A {
    void test();
}
  • 接口不容許有實例域,但能夠有常量
  • 接口中的域都會自動聲明爲public static final
  • 接口中的方法都會自動聲明爲public
  • 接口中能夠聲明抽象方法,Java 8之後還能夠聲明靜態方法和默認方法
// Java 8版本
public interface A {
    //常量
    String AUTHOR = "Yuicon";
    //抽象方法
    void test();
    //默認方法
    default void testDefault(){}
    //靜態方法
    static void testStatic(){}
}

默認方法的衝突

若是一個類實現的接口中有簽名相同的默認方法,那麼就會有衝突的問題。在Java中解決這個問題有一些明確的規則:閉包

  • 在超類中已有同簽名的方法,就會忽略接口中的默認方法,也就是超類優先
  • 接口中默認方法和另外一個默認方法或者抽象方法衝突的,必需要覆蓋這個方法

lambda表達式

lambda表達式是一個可傳遞的代碼塊,能夠在之後執行一次或屢次。之因此會有這麼一個特性,是由於原先在Java中傳遞一個代碼塊是很是繁瑣的一件事情,必需要構建一個對象。好比經常使用的Runnable接口:併發

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("我好麻煩");
    }
};

lambda版本就很是簡潔:編程語言

Runnable runnable = () -> System.out.println("我很簡潔");

是的,lambda版本只要一行就完成了任務。ide

語法

在我看來lambda表達式是一種語法糖,它提供了一種簡潔、易懂的方式來實現只有一個抽象方法的接口。關鍵詞是隻有一個抽象方法的接口,好比這樣一個接口:函數

@FunctionalInterface
public interface A {
    void test();
}

A a = () -> System.out.println("test");
a.test();

其中@FunctionalInterface註解是用來標記接口爲函數式接口,去掉也不會影響功能,添加了這個註解後編譯器會檢查接口內是否只有一個抽象方法。ui

lambda表達式主要有如下要素:this

  • 參數
  • 箭頭 ->
  • 方法體
  • 自由變量

參數

lambda表達式的參數和普通方法的參數並沒有太大區別,主要的區別點有:

  • 若是參數的類型能夠被編譯器推導出來,那麼能夠省略參數類型
  • 若是參數的類型能夠被編譯器推導出來,並且只有一個參數,那麼就能夠省略括號
Consumer<String> consumer = s -> System.out.println(s);

方法體

lambda表達式的方法體內只有一條語句的時候,能夠不加大括號且無需指定返回值,編譯器會自動推導。方法體內有多條語句的時候就須要加大括號並手動指定返回值,不過lambda表達式是沒有本身的做用域的,這點須要注意。

Supplier<String> supplier = () -> {
    String s = "test";
    return s;
};

自由變量

自由變量是指非參數並且不在方法體內定義的變量,咱們來看一個例子:

public static void main(String[] args) {
        String test = "test";
        A a = () -> System.out.println(test);
        a.test();
    }

例子中的變量test就是一個自由變量,代碼塊a引用了外部方法的變量,這就是一個閉包了。lambda表達式會複製一份自由變量的值,對象的話就是複製一個引用,所以lambda表達式離開了原做用域也能正常使用自由變量。不過lambda表達式對自由變量是有要求的,自由變量必須是不可變的,緣由是併發執行時不安全。如下代碼是錯誤的:

for (int i = 0; i < 9; i++) {
    // error
    A a = () -> System.out.println(i);
}

方法引用

方法引用是語法糖的語法糖,顧名思義方法引用是引用已有方法的一個特性。它的形式以下:

@FunctionalInterface
public interface A {

    void test(String s);

}

A a = System.out::println;
a.test("test");

之因此說方法引用是語法糖的語法糖是由於A a = System.out::println;徹底等價於A a = s -> System.out.println(s);,方法引用有5種狀況:

  • object::instanceMethod
  • this::instanceMethod
  • super::instanceMethod // 超類方法
  • Class::staticMethod
  • Class::instanceMethod

前4種狀況和lambda表達式是徹底等價的,第5種狀況比較特殊,第一個參數會成爲方法的目標。好比String::compareToIgnoreCase等同於 (x, y)-> x.compareToIgnoreCase(y)

構造器引用

構造器引用是引用對象的構造器,用的是特殊的方法名new,使用形式爲Object::new,使用方法和方法引用差很少。

經常使用函數式接口

JDK已經提供了經常使用的函數式接口基本上是不須要本身寫函數式接口的。

後記

一週一篇是不可能一週一篇的,人懶起來就和鹹魚同樣根本不會動彈。還好人是會變通的,上週少了這周補上不就好了!Java被人詬病繁瑣不是一天兩天了,在各類新生編程語言的追趕下Java也要加快本身的演進了,更改發佈週期就是一個很好的信號。

參考資料:《Java核心技術 卷1》

相關文章
相關標籤/搜索