方法引用

Shen_LAN發表於2024-07-21

方法引用有什麼用?

  1. 寫更少程式碼
  2. 提高程式碼複用性和可維護性(尤其是團隊專案中)

引用靜態方法
如果你要引用的是一個靜態方法,你可以使用類名::靜態方法的形式。例如, 將集合中String型別資料轉換成int型別
這是匿名內部類的寫法:
image.png
檢視parsInt原始碼可以發現該方法滿足靜態方法引用的條件.
image.png
因此可以直接引用該靜態方法

ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "1", "2", "3", "4", "5");

list.stream().map(Integer::parseInt).forEach(System.out::println);

引用構造方法
類名::new

什麼時候要引用構造方法?

——建立物件. 舉一個例子, 將String資料轉化成Actor

ArrayList<String> list = new ArrayList<>();
list.add("趙麗穎-21");
list.add("楊冪-23");
list.add("胡歌-23");
list.add("霍建華-22");
list.add("唐嫣-23");

list.stream().map(new Function<String, Actor>() {

    @Override
    public Actor apply(String s) {
        return new Actor(s.split("-")[0],Integer.parseInt(s.split("-")[1]));
    }
}).forEach(System.out::println);

想想這裡能否採用方法引用? 先回顧一下方法引用的條件

  1. 目標介面必須是函式式介面
  2. 引用的方法的引數列表返回型別必須與目標介面的抽象方法的引數列表和返回型別相匹配
  3. 方法體要滿足所需的功能

第一個條件滿足, 但是第二個條件沒有現有的方法去引用, 我們可以自己在Actor裡面寫一個(實際開發中很可能有別人寫好的), 寫的時候必須遵循條件2

//"楊冪-23" --> Actor, 符合apply方法中的引數和返回型別
public Actor(String s) {
String name = s.split("-")[0];
int age = Integer.parseInt(s.split("-")[1]);
this.name = name;
this.age = age;
}
//方法引用
list.stream().map(Actor::new).forEach(System.out::println);

引用成員方法
物件::成員方法適用於該方法在其他類中
類名::成員方法適用於該方法在本類中
this::成員方法
super::成員方法

練習
image.png

public Student(String s) {
this.name = s.split(",")[0];
this.age = Integer.parseInt(s.split(",")[1]);
}
//1.建立一個String型別的集合
ArrayList<String> list = new ArrayList<>();
//2.向集合中新增元素
Collections.addAll(list,"張三,21","李四,22","王五,23","趙六,21","田七,23");
//3.將集合元素型別轉換成Student, 再收集到陣列中
Student[] array = list.stream().map(Student::new).toArray(Student[]::new);
System.out.println(Arrays.toString(array));

image.png

//1.建立Student物件集合
ArrayList<Student> list = new ArrayList<>();
//2.新增學生物件
list.add(new Student("張三", 20));
list.add(new Student("李四", 21));
list.add(new Student("王五", 22));
//3.轉化成Stream流, 只獲取學生姓名, 再放入陣列
String[] array = list.stream().map(new Function<Student, String>() {
    @Override
    public String apply(Student student) {
        return student.getName();
    }
}).toArray(String[]::new);
System.out.println(Arrays.toString(array));

請問上面程式碼的map能否使用方法引用?

你可能會回答不行, 因為apply需要接收一個Student型別的引數, 但是getName()沒有任何引數, 不符合條件.
實際上, getName()方法接收了一個隱含的引數this, getName()方法體內的name欄位實際上是透過this.name訪問的, 即使你沒有在方法簽名中宣告它. 當你在Student物件上呼叫getName()時, this引數被自動設定為student物件.因此我們可以將上面的程式碼修改為

 String[] array = list.stream().map(Student::getName).toArray(String[]::new);

為什麼要用類名去引用getName?而不是物件

Student::getName能夠被應用到流中的任何Student物件. 如果你使用的是物件引用(假設myStudent是一個Student物件),如myStudent::getName,這將不會工作,因為myStudent可能不一定是流中的元素,而且這種方法引用將只繫結到myStudent這個特定物件,而不是流中的任意Student物件。
image.png
這與第二個練習類似, 不同之處在於Studnet --> "姓名-年齡", 只需要重寫Student類中的toString()方法, 透過Student::toString進行引用即可.

相關文章