Java 8 中的方法引用,輕鬆減少程式碼量,提升可讀性!

  • 2020 年 12 月 8 日
  • 筆記

1. 引言

Java8中最受廣大開發中喜歡的變化之一是因為引入了 lambda 表達式,因為這些表達式允許我們放棄匿名類,從而大大減少了樣板程式碼,並提高了可讀性。
方法引用是lambda表達式的一種特殊類型。它們通常通過引用現有方法來創建簡單的lambda表達式。

方法引用包括以下四種類型:

  • 靜態方法
  • 特定對象的實例方法
  • 特定類型的任意對象的實例方法
  • 構造方法

在本篇文章中,我們將探討Java中的方法引用。

2. 引用靜態方法

We'll begin with a very simple example, capitalizing and printing a list of Strings:

我們從一個非常簡單的示例開始,字元串轉成大寫並列印:

List<String> messages = Arrays.asList("hello", "baeldung", "readers!");

我們可以通過簡單的lambda表達式直接調用 StringUtils.capitalize() 方法:

messages.forEach(word -> StringUtils.capitalize(word));

或者,我們可以使用方法引用來簡單地引用 capitalize 靜態方法:

messages.forEach(StringUtils::capitalize);

注意,方法引用應使用::運算符。

3. 引用特定對象的實例方法

為了演示這種類型的方法引用,我們新建以下這兩個類:

public class Bicycle {
 
    private String brand;
    private Integer frameSize;
    // standard constructor, getters and setters
}
 
public class BicycleComparator implements Comparator {
 
    @Override
    public int compare(Bicycle a, Bicycle b) {
        return a.getFrameSize().compareTo(b.getFrameSize());
    }
 
}

創建一個 BicycleComparator 對象來比較自行車尺寸:

BicycleComparator bikeFrameSizeComparator = new BicycleComparator();

我們可以使用lambda表達式按尺寸大小對自行車進行排序,但需要指定兩個自行車實例進行比較:

createBicyclesList().stream()
  .sorted((a, b) -> bikeFrameSizeComparator.compare(a, b));

我們可以使用方法引用讓編譯器把句柄參數傳遞給我們:

createBicyclesList().stream()
  .sorted(bikeFrameSizeComparator::compare);

4. 引用特定類型任意對象的實例方法

這種類型的方法引用與前面的示例類似,但不必創建自定義對象來執行比較。

讓我們創建一個要排序的Integer 整數列表:

List<Integer> numbers = Arrays.asList(5, 3, 50, 24, 40, 2, 9, 18);

如果我們使用經典的 lambda 表達式,這兩個參數都需要顯式傳遞,而使用方法引用則要簡單得多:

numbers.stream()
  .sorted((a, b) -> a.compareTo(b));
numbers.stream()
  .sorted(Integer::compareTo);

儘管它仍然是一行程式碼,但是方法引用更容易閱讀和理解。

5. 引用構造函數

我們可以像在第一個例子中引用靜態方法一樣引用構造函數。唯一區別是需要使用new關鍵字。
現在我們用不同品牌的String列表創建一個Bicycle數組:

List<String> bikeBrands = Arrays.asList("Giant", "Scott", "Trek", "GT");

首先,我們將向Bicycle類添加一個新的構造函數:

public Bicycle(String brand) {
    this.brand = brand;
    this.frameSize = 0;
}

接下來,我們將使用方法引用中的新構造函數,並從原始的String列表中生成一個Bicycle數組:

bikeBrands.stream()
  .map(Bicycle::new)
  .toArray(Bicycle[]::new);

注意如何使用方法引用調用Bicycle和Array構造函數,從而使程式碼看起來更加簡潔明了。

6. 其他示例和限制

目前為止,方法引用是一個使程式碼非常清晰和易讀的好方法。但是,我們不能用它們來代替各種lambda表達式,因為它們有一些局限性。

它們的主要局限性是由於它們最大的優點:前一個表達式的輸出需要與引用的方法聲明的輸入參數匹配。

看看這個限制的例子:

createBicyclesList().forEach(b -> System.out.printf(
  "Bike brand is '%s' and frame size is '%d'%n",
  b.getBrand(),
  b.getFrameSize()));

這個簡單的例子不能用方法引用來表示,因為在我們的例子中,printf 方法需要3個參數,而使用createBicyclesList().forEach()只允許方法引用一個參數(Bicycle對象)。

最後,我們研究下,如何創建一個可以從lambda表達式引用的no-operation函數。

在本例中,我們希望使用lambda表達式而不使用其參數。

首先,創建 doNothingAtAll 方法:

private static <T> void doNothingAtAll(Object... o) {
}

因為這是一個varargs方法,它可執行在任意 lambda 表達式中,而不管引用的對象或參數的數量。我們看看它的作用:

createBicyclesList()
  .forEach((o) -> MethodReferenceExamples.doNothingAtAll(o));

7. 總結

在這篇文章中,我們學習了Java中的方法引用,以及如何使用它們來替換lambda表達式,從而提高了可讀性並闡明編程的意圖。

如果你覺得文章還不錯,記得關注公眾號: 鍋外的大佬
鍋外的大佬部落格

Tags: