亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

方法引用

通過前兩個小節對Lambda表達式的學習,本小節我們來介紹一個更加深入的知識點 —— 方法引用。通過本小節的學習,你將了解到什么是方法引用,方法引用的基礎語法,方法引用的使用條件和使用場景,方法引用的分類,方法引用的使用實例等內容。

1. 什么是方法引用

方法引用(Method References)是一種語法糖,它本質上就是 Lambda 表達式,我們知道Lambda表達式是函數式接口的實例,所以說方法引用也是函數式接口的實例。

Tips:什么是語法糖?語法糖(Syntactic sugar),也譯為糖衣語法,是由英國計算機科學家彼得·約翰·蘭達(Peter J. Landin)發明的一個術語,指計算機語言中添加的某種語法,這種語法對語言的功能并沒有影響,但是更方便程序員使用。通常來說使用語法糖能夠增加程序的可讀性,從而減少程序代碼出錯的機會。

可以將語法糖理解為漢語中的成語,用更簡練的文字表達較復雜的含義。在得到廣泛接受的情況下,可以提升交流的效率。

我們來回顧一個之前學過的實例:

實例演示
預覽 復制
復制成功!
import java.util.function.Consumer;

public class MethodReferencesDemo1 {

    public static void main(String[] args) {
        Consumer<String> consumer = s -> System.out.println(s);
        consumer.accept("只消費,不返回");
    }

}
運行案例 點擊 "運行案例" 可查看在線運行效果

運行結果:

只消費,不返回

上例是 Java 內置函數式接口中的消費型接口,如果你使用idea編寫代碼,System.out.println(s)這個表達式可以一鍵替換為方法引用,將鼠標光標放置到語句上,會彈出提示框,再點擊Replace lambda with method reference按鈕即可完成一鍵替換:

替換為方法引用后的實例代碼:

實例演示
預覽 復制
復制成功!
import java.util.function.Consumer;

public class MethodReferencesDemo1 {

    public static void main(String[] args) {
        Consumer<String> consumer = System.out::println;
        consumer.accept("只消費,不返回");
    }

}
運行案例 點擊 "運行案例" 可查看在線運行效果

運行結果:

只消費,不返回

我們看到System.out.println(s)這個表達式被替換成了System.out::println,同樣成功執行了代碼,看到這里,同學們腦袋上可能全是問號,這是什么語法?我們之前怎么沒提過?別著急,我們馬上就來講解語法規則。

2. 語法

方法引用使用一對冒號(::)來引用方法,格式如下:

類或對象 :: 方法名

上面實例中方法引用的代碼為:

System.out::println

其中System.out就是PrintStream類的對象,println就是方法名。

3. 使用場景和使用條件

方法引用的使用場景為:當要傳遞給Lambda體的操作,已經有實現的方法了,就可以使用方法引用。

方法引用的使用條件為:接口中的抽象方法的形參列表和返回值類型與方法引用的方法形參列表和返回值相同。

4. 方法引用的分類

對于方法引用的使用,通??梢苑譃橐韵?4 種情況:

  1. 對象 :: 非靜態方法:對象引用非靜態方法,即實例方法;
  2. 類 :: 靜態方法:類引用靜態方法;
  3. 類 :: 非靜態方法:類引用非靜態方法;
  4. 類 :: new:構造方法引用。

下面我們根據以上幾種情況來看幾個實例。

4.1 對象引用實例方法

對象引用實例方法,我們已經在上面介紹過了,System.out就是對象,而println就是實例方法,這里不再贅述。

4.2 類引用靜態方法

類引用靜態方法,請查看以下實例:

實例演示
預覽 復制
復制成功!
import java.util.Comparator;

public class MethodReferencesDemo2 {

    public static void main(String[] args) {
        // 使用 Lambda 表達式
        Comparator<Integer> comparator1 = (t1, t2) -> Integer.compare(t1, t2);
        System.out.println(comparator1.compare(11, 12));

        // 使用方法引用,類 :: 靜態方法( compare() 為靜態方法)
        Comparator<Integer> comparator2 = Integer::compare;
        System.out.println(comparator2.compare(12, 11));
    }

}
運行案例 點擊 "運行案例" 可查看在線運行效果

運行結果:

-1
1

查看 Java 源碼,可觀察到compare()方法是靜態方法:

我們再來看一個實例:

實例演示
預覽 復制
復制成功!
import java.util.Comparator;
import java.util.function.Function;

public class MethodReferencesDemo3 {

    public static void main(String[] args) {
        // 使用 Lambda 表達式
        Function<Double, Long> function1 = d -> Math.round(d);
        Long apply1 = function1.apply(1.0);
        System.out.println(apply1);

        // 使用方法引用,類 :: 靜態方法( round() 為靜態方法)
        Function<Double, Long> function2 = Math::round;
        Long apply2 = function2.apply(2.0);
        System.out.println(apply2);
    }

}
運行案例 點擊 "運行案例" 可查看在線運行效果

運行結果:

1
2

4.3 類引用實例方法

類引用實例方法,比較難以理解,請查看以下實例:

實例演示
預覽 復制
復制成功!
import java.util.Comparator;

public class MethodReferencesDemo4 {

    public static void main(String[] args) {
        // 使用 Lambda 表達式
        Comparator<String> comparator1 = (s1, s2) -> s1.compareTo(s2);
        int compare1 = comparator1.compare("Hello", "Java");
        System.out.println(compare1);

        // 使用方法引用,類 :: 實例方法( compareTo() 為實例方法)
        Comparator<String> comparator2 = String::compareTo;
        int compare2 = comparator2.compare("Hello", "Hello");
        System.out.println(compare2);
    }

}
運行案例 點擊 "運行案例" 可查看在線運行效果

運行結果:

-2
0

Comparator接口中的compare(T t1, T t2)抽象方法,有兩個參數,但是String類下的實例方法compareTo(String anotherString)只有 1 個參數,為什么這種情況下還能使用方法引用呢?這屬于一個特殊情況,當函數式接口中的抽象方法有兩個參數時,已實現方法的第 1 個參數作為方法調用者時,也可以使用方法引用。此時,就可以使用類來引用實例方法了(即實例中的String::compareTo)。

4.4 類引用構造方法

類引用構造方法,可以直接使用類名 :: new,請查看如下實例:

實例演示
預覽 復制
復制成功!
import java.util.function.Function;
import java.util.function.Supplier;

public class MethodReferencesDemo5 {

    static class Person {
        private String name;

        public Person() {
            System.out.println("無參數構造方法執行了");
        }

        public Person(String name) {
            System.out.println("單參數構造方法執行了");
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

    }

    public static void main(String[] args) {

        // 使用 Lambda 表達式,調用無參構造方法
        Supplier<Person> supplier1 = () -> new Person();
        supplier1.get();

        // 使用方法引用,引用無參構造方法
        Supplier<Person> supplier2 = Person::new;
        supplier2.get();

        // 使用 Lambda 表達式,調用單參構造方法
        Function<String, Person> function1 = name -> new Person(name);
        Person person1 = function1.apply("小慕");
        System.out.println(person1.getName());


        // 使用方法引用,引用單參構造方法
        Function<String, Person> function2 = Person::new;
        Person person2 = function1.apply("小明");
        System.out.println(person2.getName());
    }

}
運行案例 點擊 "運行案例" 可查看在線運行效果

運行結果:

無參數構造方法執行了
無參數構造方法執行了
單參數構造方法執行了
小慕
單參數構造方法執行了
小明

在實例中,我們使用了Lambda表達式和方法引用兩種方式,分別調用了靜態內部類Person的無參和單參構造方法。函數式接口中的抽象方法的形參列表與構造方法的形參列表一致,抽象方法的返回值類型就是構造方法所屬的類型。

5. 小結

通過本小節的學習,我們知道了方法引用是一個語法糖,它本質上還是Lambda表達式。方法引用使用一對冒號(::)來引用方法。要傳遞給Lambda體的操作,已經有實現的方法了,就可以使用方法引用;想要使用方法引用,就要求接口中的抽象方法的形參列表和返回值類型與方法引用的方法形參列表和返回值相同。方法引用可能較為抽象,希望同學們課下多加練習。