天天看點

Java函數式程式設計:Lambda表達式和函數式接口

Lambda表達式

Java的Lambda表達式形式為:

(params) -> { statements };
           

其中:

  1. 若params隻有1個,可以省略():

    param -> { statements };

  2. 若params為0個,用()表示無參數:

    () -> { statements };

  3. 若代碼為一行,則可省略{},無論該行代碼是否為表達式:

    (params) -> expression;

    (params) -> statement;

函數式接口

Lambda表達式本質上是一個對象。而對象就必然有其對應的類型,就像一個字元串其類型為String一樣。

當一個函數,其參數需要傳入一個Lambda表達式時,函數的參數就需要明确定義該Lambda表達式的類型。

實際上,Java的Lambda表達式都是歸屬于某個具體已定義的類型的。根據Lambda表達式的參數與傳回值數量和類型,Java定義了一組interface來接收Lambda表達式,稱之為函數式接口。

函數式接口有以下條件:

  1. 是一個interface。
  2. 有且僅有一個抽象方法。

另外:

  • interface預設繼承Java.lang.Object,故Object的抽象方法不包含在内。
  • 可以使用@FunctionalInterface注解來标記函數式接口,有助于編譯器進行檢查。但@FunctionalInterface注解并非必須,隻要滿足條件,即使沒有@FunctionalInterface注解編譯器也會将接口識别為函數式接口。

例如:

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
           

Java8以後,Runnable接口多了@FunctionalInterface注解。這表示Runnable接口成為函數式接口了,可以接收Lambda表達式。

而實際上,由于Runnable接口有且僅有一個抽象方法,滿足函數式接口的條件,是以即使不加@FunctionalInterface注解也依然會被識别為函數式接口。

是以可以自定義函數式接口:

@FunctionalInterface
public interface MyInterface {
    // 定義唯一的抽象方法
    void doWork();
    // toString()屬于Object,不包含
    String toString();
}
           

使用:

MyInterface myInterface = () -> System.out.println("do work");
           

或将其作為參數類型:

class MyClass {
  public void doMyWork(MyInterface myInterface) {
    myInterface.doWork();
  }
}

MyClass myClass = new MyClass();
myClass.doMyWork(() -> System.out.println("do work"));
           

前面提到的Runnable接口同理。

除了Runnable接口,Java8還提供了一組函數式接口:

接口 參數 傳回類型 描述
Predicate T boolean 判斷一個對象是否符合條件。常用于對象過濾。
Function<T, R> T R 将一個對象轉換為另一個對象。
Supplier None T 提供一個對象。生産者-消費者中的生産者。
Consumer T void 消費一個對象。生産者-消費者中的消費者。無傳回值。
UnaryOperator T T 接收對象并傳回同類型的對象。
BinaryOperator (T, T) T 接收兩個對象,并傳回一個原對象。這三個對象的類型都相同。

其中:

  1. Supplier是唯一無參數的函數。其他函數都必須傳入參數。
  2. Consumer是唯一無傳回的函數。其他函數都會傳回一個對象。

Runnable接口則是無參數,無傳回值的。