相關閱讀:java
JAVA基礎(一)簡單、透徹理解內部類和靜態內部類
JAVA基礎(二)內存優化-使用Java引用作緩存
JAVA基礎(三)ClassLoader實現熱加載
JAVA基礎(四)枚舉(enum)和常量定義,工廠類使用對比
JAVA編程思想(一)經過依賴注入增長擴展性
JAVA編程思想(二)如何面向接口編程
JAVA編程思想(三)去掉彆扭的if,自注冊策略模式優雅知足開閉原則
JAVA編程思想(四)Builder模式經典範式以及和工廠模式如何選?
HikariPool源碼(二)設計思想借鑑
人在職場(一)IT大廠生存法則編程
接口中只有一個抽象方法的接口稱爲函數式接口。函數式接口能夠經過@FunctionalInterface註解來修飾,也能夠不使用該註解,只要接口只有一個抽象方法則可。緩存
import java.util.function.Consumer;
public class Service {
// 方法入參爲函數式接口,函數定義爲消費Event
public void exec(Consumer<Event> consumer) {
Event event = new Event(1001, "get data from cache.");
consumer.accept(event);
}
}
// event通知消費類,消費event的方式是將event發出去
public class EventNotify {
// 方法名並不重要,不須要和函數式接口方法名一致,只要參數和返回值一致則可
public void send(Event event) {
System.out.println("send event: " + event.toString());
}
}
// event記錄消費類,消費event的方式是將event記錄下來
public class EventLogger {
// 方法名並不重要,不須要和函數式接口方法名一致,只要參數和返回值一致則可
public static void log(Event event) {
System.out.println("log event: " + event.toString());
}
}
public class Event {
private int eventId;
private String content;
public Event(int eventId, String content) {
this.eventId = eventId;
this.content = content;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("eventId=").append(eventId).append(", ");
builder.append("content=").append(content);
return builder.toString();
}
}
複製代碼
public class FunctionDemo {
public static void main(String[] args) {
Service service = new Service();
EventNotify eventNotify = new EventNotify();
// 使用類實例::方法名傳入函數,這裏必須使用類實例,是由於這個方法聲明爲非靜態方法
service.exec(eventNotify::send);
// 使用類::方法名傳入函數,這裏可使用類而非類實例,是由於這個方法聲明爲靜態方法
service.exec(EventLogger::log);
}
}
複製代碼
輸出:app
send event: eventId=1001, content=get data from cache.
log event: eventId=1001, content=get data from cache.
複製代碼
看到這裏,不少人會想,從這個例子看,函數式接口並無什麼優點,不用函數式接口,使用接口實現類也能實現啊.ide
class NonFunctionEventNotify<Event> implements Consumer<Event> {
@Override
public void accept(Event event) {
System.out.println("send event: " + event.toString());
}
}
// 事件記錄類,消費event的方式是將event記錄下來
class NonFunctionEventLogger<Event> implements Consumer<Event> {
@Override
public void accept(Event event) {
System.out.println("log event: " + event.toString());
}
}
複製代碼
public class FunctionDemo {
public static void main(String[] args) {
Service service = new Service();
NonFunctionEventNotify nonFunctionEventNotify = new NonFunctionEventNotify();
// 傳入實現類實例
service.exec(nonFunctionEventNotify);
NonFunctionEventLogger nonFunctionEventLogger = new NonFunctionEventLogger();
// 傳入實現類實例
service.exec(nonFunctionEventLogger);
}
}
複製代碼
可見,不用函數式接口也能實現對應功能,那爲啥還要使用函數式接口呢?函數
差別以下:post
實現方式 | 摻入參數 | 實現接口 | 方法名 |
---|---|---|---|
函數式接口 | 可直接傳入靜態方法,省去類實例化動做 | 不須要實現任何接口 | 沒有要求,只要方法入參和出參同樣則可 |
接口實現類 | 必須有類實例,傳入類實例的方法 | 須要實現指定接口 | 必須同接口方法徹底同樣 |
經過對比,函數式接口的優點就大大致現出來了。優化
- 方法入參到方法級,粒度比類小,這樣自由度更高,更靈活,就比如拼樂高,零件粒度越小,越能充分發揮想象力,進行自由組合。
- 不要求必須實現某個接口,對方法名也沒有要求,只要知足函數接口的入參和出參定義就行,這樣已有的代碼可能不用修改就能被利用,特別是當你手裏沒有源碼可改時這點顯得尤其重要(雖然能夠寫個包裝類去作適配,但能不寫不是更好麼)
- 函數式接口能夠作到方法級複用,自由度更高,更靈活
- 函數式接口的方法定義和調用者實現方法定義解耦,不須要方法一致,也不要求實現該接口。
- 方法定義解耦後,只要方法的入參和出參一致時,就能夠複用已有代碼,避免修改,複用性大大提升。
注:Java提供的函數式接口已經能知足大部分使用場景,這些接口能夠在java.util.function包下找到。ui
end.this