Java 常用的 Functional Interface:Consumer、Function、Predicate、 Supplier

在看 Functional Interface 之前,先看看以下例子,如果我們只能寫一個方法,要在一個字串集合內,找到預期的字串,同時要能支援完全比對/忽略大小寫的話,寫出來的程式碼大概長的是以下這個樣子:


/*
* expect:是要比對的字串。
* ignoreCase:true 代表比對可以忽略大小寫。
*/
public boolean findAnyMatch(List<String> list, String expect, boolean ignoreCase)
{
  if (ignoreCase) // 若任一個字串符合傳入的字串(忽略大小寫),就回傳 true。
  {
    for (String string : list)
    {
      if (expect.equalsIgnoreCase(string))
      {
        return true;
      }
    }
  }
  else // 若任一個字串符合傳入的字串(要完全一樣),就回傳 true。 
  {
    for (String string : list)
    {
      if (expect.equals(string))
      {
        return true;
      }
    }
  }
		
  return false;
}

...

System.out.println(foo.findAnyMatch(list, "Moe", false)); // true
System.out.println(foo.findAnyMatch(list, "Jack", false)); // false
System.out.println(foo.findAnyMatch(list, "moe", true)); // true

原本的寫法除了又臭又長之外,還有多重巢狀結構,不容易閱讀。

同樣的方法改用 lamda 表達式作為 findAnyMatch 傳入參數的寫法,程式碼如下,是不是短了很多?甚至還可以在不更動 findAnyMatch 的情況下,找出是否有包含 "o" 的字串。


/*
* predicate:是一個只定義一個抽象方法 interface,告訴傳入的 Lambda 表達式會收到一個參數,然後要回傳一個 boolean。
*/
public boolean findAnyMatch(List<String> list, Predicate<String> predicate)
{
  // 若任一個字串符合傳入的 predicate 檢查的條件,就回傳 true。
  return list.stream()
    .filter(predicate) // 傳入的 lambda 會收到一個字串,回傳 boolean
    .findFirst()
    .isPresent();
}

...

System.out.println(foo.findAnyMatch(list, (s) -> s.equals("Moe"))); // true
System.out.println(foo.findAnyMatch(list, (s) -> s.equals("Jack"))); // false
System.out.println(foo.findAnyMatch(list, (s) -> s.equalsIgnoreCase("moe"))); // true
System.out.println(foo.findAnyMatch(list, (s) -> s.contains("o"))); // true

所以大致的演進流程是:用傳入的參數來影響方法內的行為 > 直接把行為(lambda)當作參數。同樣的效果用傳入匿名類別當作參數也可以做到,但傳入 lambda 的寫法看起來比較短、比較好閱讀。

Java 把 lambda 寫法常用到的 Interface 先定義出來,我們在導入 lamda 寫法改寫原有方法時,就不用重複定義這些常出現的介面。比如上面用到的 Predicate 介面,它預期接收一個字串類型物件,然後返回一個 boolean 值。

以下整理出程式碼常見到的 Functional Interface:

Functional Interface輸入回傳
PredicateT 類型物件boolean
ConsumerT 類型物件不用回傳
FuctionT 類型物件R 類型物件
SupplierT 類型物件
BiPredicateT 類型物件 + U 類型物件boolean
BiConsumerT 類型物件 + U 類型物件不用回傳
BiFuctionT 類型物件 + U 類型物件R 類型物件

0 則回應: