Java 8 習慣用語,第 7 部分 函式介面
原文地址:https://www.ibm.com/developerworks/cn/java/j-java8idioms7/index.html
Java 8 習慣用語,第 7 部分
函式介面
瞭解如何建立自定義函式介面,以及為什麼應儘量使用內建介面
lambda 表示式的型別是什麼?一些語言使用函式值或函式物件來表示 lambda 表示式,但 Java™ 語言沒有這麼做。Java 使用函式介面來表示 lambda 表示式型別。乍一看似乎有點奇怪,但事實上這是一種確保對 Java 語言舊版本的向後相容性的有效途徑。
您應該非常熟悉下面這段程式碼:
Thread
thread = new Thread(new Runnable() { public
void run() {
System.out.println("In
another thread");
}
});
thread.start();
System.out.println("In
main");
|
Thread
類和它的建構函式是在 Java 1.0 中引入的,距今已有超過 20 年的時間。從那時起,建構函式從未改變過。將 Runnable
的匿名例項傳遞給建構函式已成為一種傳統。但是從
Java 8 開始,可以選擇傳遞 lambda 表示式:
Thread
thread = new Thread(() -> System.out.println("In another thread")); |
Java 8 是自 Java 語言誕生以來進行的一次最重大更新—包含了非常豐富的新功能,您可能想知道從何處開始著手瞭解它。在本系列中,作家兼教師 Venkat Subramaniam 提供了一種慣用的 Java 8 程式設計方法:這些簡短的探索會激發您反思您認為理所當然的 Java 約定,同時逐步將新技術和語法整合到您的程式中。
Thread
類的建構函式想要一個實現 Runnable
的例項。在本例中,我們傳遞了一個
lambda 表示式,而不是傳遞一個物件。我們可以選擇向各種各樣的方法和建構函式傳遞 lambda 表示式,包括在 Java 8 之前建立的一些方法和建構函式。這很有效,因為 lambda 表示式在 Java 中表示為函式介面。
函式介面有 3 條重要法則:
- 一個函式介面只有一個抽象方法。
-
在
Object
類中屬於公共方法的抽象方法不會被視為單一抽象方法。 - 函式介面可以有預設方法和靜態方法。
任何滿足單一抽象方法法則的介面,都會被自動視為函式介面。這包括 Runnable
和 Callable
等傳統介面,以及您自己構建的自定義介面。
內建函式介面
除了已經提到的單一抽象方法之外,JDK 8 還包含多個新函式介面。最常用的介面包括 Function<T, R>
、Predicate<T>
和Consumer<T>
,它們是在 java.util.function
包中定義的。Stream
的 map
方法接受 Function<T,
R>
作為引數。類似地,filter
使用 Predicate<T>
,forEach
使用 Consumer<T>
。該包還有其他函式介面,比如 Supplier<T>
、BiConsumer<T,
U>
和 BiFunction<T, U, R>
。
可以將內建函式介面用作我們自己的方法的引數。例如,假設我們有一個 Device
類,它包含方法 checkout
和 checkin
來指示是否正在使用某個裝置。當用戶請求一個新裝置時,方法 getFromAvailable
從可用裝置池中返回一個裝置,或在必要時建立一個新裝置。
我們可以實現一個函式來借用裝置,就象這樣:
public
void borrowDevice(Consumer< Device >
use) {
Device
device = getFromAvailable();
device.checkout();
try
{
use.accept(device);
}
finally {
device.checkin();
}
}
|
borrowDevice
方法:
-
接受
Consumer<Device>
作為引數。 - 從池中獲取一個裝置(我們在這個示例中不關心執行緒安全問題)。
-
呼叫
checkout
方法將裝置狀態設定為 checked out。 - 將裝置交付給使用者。
在完成裝置呼叫後返回到 Consumer
的 accept
方法時,通過呼叫 checkin
方法將裝置狀態更改為 checked
in。
下面給出了一種使用 borrowDevice
方法的方式:
new
Sample().borrowDevice(device -> System.out.println("using " + device));
|
因為該方法接收一個函式介面作為引數,所以傳入一個 lambda 表示式作為引數是可以接受的。
自定義函式介面
儘管最好儘量使用內建函式介面,但有時需要自定義函式介面。
要建立自己的函式介面,需要做兩件事:
-
使用
@FunctionalInterface
註釋該介面,這是 Java 8 對自定義函式介面的約定。 - 確保該介面只有一個抽象方法。
該約定清楚地表明該介面應接收 lambda 表示式。當編譯器看到該註釋時,它會驗證該介面是否只有一個抽象方法。
使用 @FunctionalInterface
註釋可以確保,如果在未來更改該介面時意外違反抽象方法數量規則,您會獲得錯誤訊息。這很有用,因為您會立即發現問題,而不是留給另一位開發人員在以後處理它。沒有人希望在將
lambda 表示式傳遞給其他人的自定義介面時獲得錯誤訊息。
建立自定義函式介面
作為一個示例,我們將建立一個 Order
類,它有一系列 OrderItem
以及一個轉換並輸出它們的方法。我們首先建立一個介面。
下面的程式碼將建立一個 Transformer
函式介面。
@FunctionalInterface
public
interface Transformer< T >
{
T
transform(T input);
}
|
該介面用 @FunctionalInterface
註釋做了標記,表明它是一個函式介面。因為該註釋包含在 java.lang
包中,所以沒有必要匯入。該介面有一個名為 transform
的方法,後者接受一個引數化為 T
型別的物件,並返回一個相同型別的轉換後物件。轉換的語義將由該介面的實現來決定。
這是 OrderItem
類: