57.方法引用

方法引用

方法引用其實就是把已存在的類的方法直接拿過使用,當需要使用的方法自己沒有,但是其他的對象或者類有,直接借別人的在虛擬機中就是將指示引用的方法拿過來入棧使用!

一、方法引用符

雙冒號::為引用運算符,而它所在的表達式被稱為方法應用。如果Lambda要表達的函數(shù)方案已經(jīng)存在于某個方法的實現(xiàn)中,那么則可以通過雙冒號來引用該方法作為Lambda的替代者。

1.語義分析

例中,System.out對象中有一個重載的println(String)方法恰好就是我們所需要的。那么對于printString方法的函數(shù)式接口參數(shù),對比下面兩種寫法,完全等效:

  • Lambda表達式寫法:s -> System.out.println(s);
  • 方法引用寫法:System.out::println

第一種語義是指:拿到參數(shù)之后經(jīng)Lambda之手,繼而傳遞給 System.out.println 方法去處理
第二種等效寫法的語義是指:直接讓 System.out 中的 println 方法來取代Lambda。兩種寫法的執(zhí)行效果完全一樣,而第二種方法引用的寫法復用了已有方案,更加簡潔。

注:Lambda 中 傳遞的參數(shù) 一定是方法引用中 的那個方法可以接收的類型,否則會拋出異常

2.省略與推導

如果使用Lambda,那么根據(jù)“可推導就是可省略”的原則,無需指定參數(shù)類型,也無需指定的重載形式——它們都將被自動推導。而如果使用方法引用,也是同樣可以根據(jù)上下文進行推導。

函數(shù)式接口是Lambda的基礎,而方法引用是Lambda的孿生兄弟

下面這段代碼將會調用 println 方法的不同重載形式,將函數(shù)式接口改為int類型的參數(shù):由于上下文變了之后可以自動推導出唯一對應的匹配重載,所以方法引用沒有任何變化:

@FunctionalInterface
interface PrintableInteger{
    void print(int str);
}

public class Test {

    public static void printInetger(PrintableInteger data){
        data.print(1024);
    }

    public static void main(String[] args) {
        printInetger(System.out::println);
    }
}

這次方法引用將會自動匹配到 println(int) 的重載形式。

二、通過對象引用成員方法

這是最常見的一種用法,與上例相同。如果一個類中已經(jīng)存在了一個成員方法:

class MethodRefOject {
    public void printUpperCase(String str){
        System.out.println(str.toUpperCase());
    }
}

函數(shù)式接口仍然定義為:

@FunctionalInterface
interface Printable{
    void print(String str);
}

那么當需要使用這個 printUpperCase 成員方法來替代 Printable 接口的Lambda的時候,已經(jīng)具有了MethodRefObject 類的對象實例,則可以通過對象名引用成員方法,代碼為:

public class Test {

    public static void printString(Printable lambda){
        lambda.print("hello");
    }

    public static void main(String[] args) {
        printString(new MethodRefOject()::printUpperCase);
    }
}

三、通過類名稱引用靜態(tài)方法

由于在 java.lang.Math 類中已經(jīng)存在了靜態(tài)方法 abs ,所以當我們需要通過Lambda來調用該方法時,有兩種寫法。首先是函數(shù)式接口:

@FunctionalInterface
interface Calcable{
    int calc(int num);
}
public class Test {

    public static void method(int num,Calcable lambda){
        System.out.println(lambda.calc(num));
    }

    public static void main(String[] args) {
        method(-10, Math::abs);
        method(-10, n -> Math.abs(n)); // Lambda寫法!
    }
}

四、通過super引用成員方法

如果存在繼承關系,當Lambda中需要出現(xiàn)super調用時,也可以使用方法引用進行替代。首先是函數(shù)式接口:

@FunctionalInterface
interface Greetable{
    void greet();
}

class Human {
    public void sayHello() {
        System.out.println("Hello, I an father!");
    }
}

class Man extends Human{
    @Override
    public void sayHello() {
        System.out.println("Hello, I an son");
    }

    public void method(Greetable g){
        g.greet();
    }

    public void show() {

        // 新建父類對象調用
        method(() -> new Human().sayHello());

        // Lambda當前訪問
        method(() -> super.sayHello());

        // 方法引用,直接把別人的方法拿過來給自己用
        method(super::sayHello);
    }
}


public class Test {
    public static void main(String[] args) {
        new Man().show();
    }
}

五、通過this引用成員方法

this代表當前對象,如果需要引用的方法就是當前類中的成員方法,那么可以使用“this::成員方法”的格式來使用方法引用。

對于當前問題,下面就演示這種多此一舉的方法

@FunctionalInterface
interface Richable{
    void buy();
}

class Husband {
    private void buyHouse() {
        System.out.println("買套房子");
    }

    private void marry(Richable lambda) {
        lambda.buy();
    }

    public void beHappy() {
        this.buyHouse();
        marry(() -> this.buyHouse());
        marry(this::buyHouse);
    }
}

六、類的構造器的引用

由于構造器的名稱與類名完全一樣,并不固定。所以構造器引用使用 類名稱::new的格式表示

@FunctionalInterface
interface PBuilder{
    P buildP(String name);
}

class P{
    String name;
    public P(String name) { this.name=name; }
}

public class Test {
    public static void function(String name, PBuilder pBuilder){
        System.out.println(pBuilder.buildP(name).name);
    }
    public static void main(String[] args) {
        function("弦子",name -> new P(name));
        function("劉亦菲",P::new);
    }
}

七、數(shù)組的構造器應用

@FunctionalInterface
interface ArrayBuilder{
    int[] buildArray(int length);
}

public class Test {
    public static int[] initArray(int length, ArrayBuilder builder){
        return builder.buildArray(length);
    }
    public static void main(String[] args) {
        initArray(5, length -> new int[length]);
        initArray(5,int[]::new);
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容