簡單理解Java的functional interface

2021/06/26閱讀時間約 6 分鐘
在學Java的人也許多少人會跟小的一樣不是很理解functional interface到底在幹嘛,也覺得語法很多此一舉。希望能夠在此拋磚引玉。

先講結論

重點說在前面。我的理解是,functional interface是為了讓Java像是Python或R等其他語言一樣,可以把一個方法直接分離出來向物件或者參數使用,而不是只能呼叫。請聽小的娓娓道來。

再講原因: 簡單來說是Java對於OOP的堅持的副作用

因為Java是個非常堅持物件導向(Object-oriented programming)的語言,所以語法不會像Javascript, R或Python一樣可以直接定義函數於global environment中:
R的語法:
hello <- function(x) { print('hello')} 然後hello()就可以呼叫
Python的語法:
def hello():
  print("hello")
一樣直接用hello()就可以呼叫
注意到在R跟Python中 hello()都不必掛在某個特定的class中,這點Java等非常強調物件導向的語言就做不到,所以Java提出了functional interface的方案。

先複習一下什麼是interface跟functional interface

interface(介面)一開始被設計出來的功能是為了定規格。在開發程式的時候為了保證某些物件的方法名稱在同一個團隊中的不同開發者能夠維持一致,所以大家會先討論好某些規格,再定義抽象方法出來。抽象方法就是只有名稱,裡面沒有程式碼,而且強迫所有使用該interface的class必須寫出來的方法,否則編譯器就不給過(雖然新版的Java允許Interface裡面定義非抽象方法,例如private跟static methods)。這麼一來團隊就可以把使用同一個interface的方法分給不同人員去開發。
例如某團隊在開發某個網頁程式可以允許使用者利用Google, Facebook, Line帳號來登入,那麼假設這三個不同的登入方式分別是不同的class,也由不同的開發者來撰寫程式碼,那麼如果定義一個interface,叫IAuthenticate裡面有個抽象方法auth(),那麼前端負責開發登入頁面的人員就不需要知道這三個class分別到底怎麼寫,只要知道反正可以呼叫auth()方法就對了。
functional interface的定義是「只有一個抽象方法的interface」。乍看之下這個定義真的很廢,也很令人費解,感覺好像是在限縮interface的能力,從能夠定義很多方法到只能定義一個。

functional interface的用意其實跟interface不一樣

functional interface就是要解決Java沒有辦法簡單地像其他語言一樣把方法當物件傳給其他物件使用或者到時候再決定要不要執行的問題。以前述hello()的例子來說,如果按照Java物件導向的邏輯本來應該寫在某個class裡面:
class HelloPrinter() {
  public void hello() {
    System.out.println("hello!");
  }
}
使用的時候主程式裡面要這樣寫
new HelloPrinter().hello();
整個感覺很多此一舉
所以functional interface就出現了,若我們只管hello()這個方法吃進去什麼跟吐出來什麼(什麼都不吃,也沒有回傳什麼),那麼我們可以定義一個廣用的functional interface,名稱不重要,反正就這個functional interface的方法不接收參數,也不回傳東西,那麼
interface IVoider() {
  void nameDoesntMatter();
}
這個時候就直接傳lambda 方法就好
IVoider hello = () -> {System.out.println("hello");}
訂完IVoider這個functional interface之後,這個所有不吃參數並且不回傳物件的方法是都可以這樣寫存取程物件了,此處hello變成了一個到時候可以當參數傳進別的class的物件了。跟前述的R或Python寫法是否有異曲同工之妙呢?
重點是因為訂了IVoider這個functional interface,所以可以做出一堆所有不吃參數並且不回傳物件的方法是都可以這樣寫存取程物件
IVoider hey = () -> {System.out.println("hey");}
IVoider helloWorld= () -> {System.out.println("helloWorld");}
由此可見,對於functional interface而言,反正使用的時候是直接去指定lambda方法,或method reference(在其他文章中將解釋),所以方法名稱根本沒差,也因此不像interface是用來規範的。

一些Java已經幫你定義好的functional interface

既然functional interface的重點是要像其他語言那樣把方法變成可以傳進別的class的物件,而且方法名稱不重要,只有吃什麼跟吐什麼重要,那麼可以見得當然會有一些已經定義好的functinoal interfaces可以使用。
1. Predicate<T>: 吃 一個任意類型物件當參數,吐出boolean
可以想見常常在需要過濾某些東西的filter中使用。
Predicate<String> sFilter = (s) <- s.startsWith("S");
sFilter變成了一個可以快速檢查字串是不是S開頭的過濾器
2. Consumer<T>: 吃一個任意物件當參數,不吐東西(void)
3. Supplier<T>: 不吃參數,吐出一個任意物件類型T
4. UnaryOperator<T>吃一個物件,吐出一個相同的物件
5. BiFunction<T, T, U>吃兩個類型相同物件,再吐出一個不一定相同類型的物件,會用在Map裡面用來有條件地更新Collection元素中。(待其他文章詳細說明)
以上。

為什麼會看到廣告
    8會員
    15內容數
    對工程師友善的(目前免費)英文教材 #工程師 #Coding #Python #Django #English #英文 #文法 #語言學習 #程式
    留言0
    查看全部
    發表第一個留言支持創作者!
    從 Google News 追蹤更多 vocus 的最新精選內容