Java8學(xué)習(xí)筆記--Lambda表達(dá)式,F(xiàn)unctional接口,方法引用

主要內(nèi)容


  • Lambda表達(dá)式
  • Functional接口
  • 方法引用

1.Lambda表達(dá)式

Lambda表達(dá)式這個(gè)新特性也許是Java8最受歡迎的一個(gè),以至于在Android Studio還沒有提供正式支持的時(shí)候,就有開發(fā)出的插件來兼容。

本人最先接觸Lambda表達(dá)式是在Python中,眾所周知Python語言是極其簡潔的,Lambda表達(dá)式更是為其提供了巨大貢獻(xiàn)。不過第一次接觸的時(shí)候,感覺這種寫法非常奇怪,但是寫的多了以后,就會(huì)體會(huì)到Lambda表達(dá)式的巨大魅力。

由于本人現(xiàn)在主要是Android開發(fā),所以就從Android代碼中舉例。個(gè)人認(rèn)為在Android開發(fā)中有兩個(gè)比較煩人的事情。第一個(gè)是findViewById(),只要不借用其他手段,寫一個(gè)界面都要寫大量的findViewById(),費(fèi)事費(fèi)力,但又少不了。第二個(gè)就是各種事件的綁定,如:

tv.setOnClickListener(new OnClickListener() {

        @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                
            }
        });

可能我就是想顯示一個(gè)Toast,或者其他的簡單的點(diǎn)擊事件,就要寫這么一大塊代碼,當(dāng)然你也可以不用匿名內(nèi)部類,但是隨著整個(gè)類的擴(kuò)大,以后看個(gè)代碼,跳來跳去的,不勝其煩。

而且這兩個(gè)問題對(duì)于Android初學(xué)者來說,都是一開始都會(huì)遇見的,估計(jì)學(xué)的時(shí)候也是看的一臉懵逼。(??ˇ?ˇ??)

好在程序員們是永不滿足的,第一個(gè)問題在Android中已經(jīng)有了很好的解決方法,第二個(gè)問題也在Java8中得到了妥善解決,解決方法就是我們今天學(xué)習(xí)的Lambda表達(dá)式。

首先官方在文檔中就承認(rèn):
One issue with anonymous classes is that if the implementation of your anonymous class is very simple, such as an interface that contains only one method, then the syntax of anonymous classes may seem unwieldy and unclear.
既然是笨重和不清楚的,那么Lambda如何來解決呢?為了使不接觸Android開發(fā)的童鞋也能理解,我在這里模擬幾個(gè)類:

interface MyListener {
    void doSomething(String name);
}

public class MyButton {
    private MyListener listener;

    public void setListener(MyListener listener) {
        this.listener = listener;
    }

    public void click(String name){
        listener.doSomething(name);
    }

}

public static void main(String[] args){
        MyButton button = new MyButton();
        button.setListener(new MyListener() {
            @Override
            public void doSomething(String name) {
                out.print("hello " + name);
            }
        });
        button.click("jack");
}

先看看setListener這一代碼塊中哪些是多余的,也就是對(duì)我們的邏輯不重要的。如果不清楚地話,可以回想一下,如果用的是IntelliJ之類的IDE,使用代碼提示后,IDE自動(dòng)幫我們補(bǔ)全了哪些代碼(什么?你一直用記事本開發(fā)項(xiàng)目,好吧,我敬你是條好漢,請(qǐng)忽略這些,它不適用你現(xiàn)在的境界)。對(duì),當(dāng)你在括號(hào)內(nèi)寫個(gè)new,再寫個(gè)首字母,回車一敲。OK!除了那行輸出語句,其他的都給你寫了,有的甚至還幫你加一行注釋。

這也就清楚地告訴了我們,除了那條輸出語句,也就是你自己的邏輯,其他的東西都是千篇一律,你就是寫出朵花來也還是這些東西。既然IDE可以幫我們做這些東西,那么我們?yōu)楹尾荒軐懞唵我恍?,那些一樣的東西在編譯的時(shí)候,讓編譯器幫我們補(bǔ)上呢,或者編譯器理解是什么意思就行,補(bǔ)不補(bǔ)都無所謂。于是Lambda表達(dá)就應(yīng)運(yùn)而生。

那么我們看看上面那段代碼用Lambda表達(dá)式怎么寫:

button.setListener( (String name) -> {out.print("hello "+name);} );

對(duì),就是這么簡潔,一行搞定(學(xué)完后面的方法引用時(shí),會(huì)更加簡潔)。
這里就引出了Lambda表達(dá)式的語法:

(Type1 param1, Type2 param2, ..., TypeN paramN) -> {
  statment1;
  statment2;
  //.............
  return statmentM;
}

前面是原來方法的參數(shù),中間一個(gè)箭頭,后面是要執(zhí)行的邏輯,就這么清晰簡單。

當(dāng)然,如果像我剛才舉的例子一樣,一行語句,那么花括號(hào)可以省略,結(jié)尾引號(hào)也可以省略(多于一個(gè)則不可):

button.setListener((String name)->out.print("hello "+name));

有同學(xué)可能會(huì)問,那參數(shù)前面的類型是不是必要的呢?對(duì),那個(gè)類型,不是我們使用的時(shí)候所決定的,是在定義的時(shí)候就已經(jīng)決定的,所以在這里就算給寫成另外一種類型也用不了,所以它也是多余的,也可以省略(特別的,如果只有一個(gè)參數(shù),外面的那個(gè)小括號(hào)也可以省略,但是如果沒有參數(shù),必須放一個(gè)空括號(hào))。

button.setListener(name -> out.print("hello " + name))

2.Functional接口

首先需要引入一個(gè)概念--函數(shù)式接口。簡單的來說就是只含一個(gè)方法的普通接口。為什么要只含一個(gè)方法?目的就是為了更好地支持Lambda表達(dá)式,如果一個(gè)接口中有多個(gè)方法,是不可以使用Lambda表達(dá)式,因?yàn)長ambda表達(dá)式隱去了方法名和參數(shù)類型,無法確認(rèn)到底使用的是哪個(gè)方法。

更重要的是,對(duì)于現(xiàn)有的能很好支持Lambda表達(dá)式的接口,一旦后期添加方法,就不再是函數(shù)式接口,很容易導(dǎo)致大量代碼編譯不過。所以,Java8提供了@FunctionalInterface的注解,來標(biāo)注這個(gè)接口為函數(shù)式接口。對(duì)于添加了該注解的方法,如果有多個(gè)抽象方法,將會(huì)直接報(bào)錯(cuò):

FunctionalInterface.png

注意:雖然函數(shù)式接口只能有一個(gè)抽象方法,但是不影響有默認(rèn)方法和靜態(tài)方法,應(yīng)為這兩種方法不是默認(rèn)重寫的,在Lambda表達(dá)式中應(yīng)用是不會(huì)出錯(cuò)的:

interface.png

作為對(duì)應(yīng)的更新,Java8也為我們提供了大量現(xiàn)成的函數(shù)式接口方便使用,他們大多數(shù)邏輯都比較簡單,大家就算不使用在需要的時(shí)候也可以自己寫出來。

可以參考這里:Java8 函數(shù)式接口

3.方法引用

Java8是將Lambda表達(dá)式作為更新的一個(gè)重頭戲,所以為我們提供的當(dāng)然不僅僅只是上面的特性,更是提供了方法引用這種語法,將Lambda表達(dá)式的簡潔發(fā)揮到了極致。

在介紹什么是方法引用時(shí),我們先看一個(gè)例子。前面我提供的第一個(gè)Lambda表達(dá)式后說,下面這個(gè)語句可以更加簡潔:

button.setListener( (String name) -> {out.print("hello "+name);} );

是時(shí)候兌現(xiàn)承諾了:

button.setListener(out::println);

是不是更加簡(sang)潔(xin)明(bing)了(kuang),感覺這次Java8誓要將簡潔這條路走到底啊。

廢話少說,這就是方法引用的一種形式--引用靜態(tài)方法。它借鑒了類似c++中的操作符::,意思是相似的,表示操作符后面的方法是屬于誰的。

為什么要添加方法引用這種語法呢?可以這樣想,原來Lambda表達(dá)式的箭頭左邊是方法的參數(shù),右邊是代碼邏輯,為了減少代碼臃腫和提高復(fù)用,我們往往會(huì)專門寫一個(gè)方法,然后傳入左邊的參數(shù)執(zhí)行即可。也就是類似下面形式:

((arg1,arg2,...) -> fun(arg1,arg2,...) )

這里的關(guān)鍵就是那個(gè)函數(shù),參數(shù)不是我們所能改變的,而我們?cè)诖怂龅膬H僅是將參數(shù)傳入某個(gè)方法,某種意義來說,這也是一種多余的操作,能不能我們?cè)谶@里指定要用的是哪個(gè)方法,編譯器幫我們完成這個(gè)動(dòng)作呢?

答案當(dāng)然是可以的,利用的就是方法引用這一新語法。

方法引用一共有下面四種形式:

類型 示例
引用靜態(tài)方法 ContainingClass::staticMethodName
引用某個(gè)對(duì)象的實(shí)例方法 containingObject::instanceMethodName
引用某種類型的任意對(duì)象的實(shí)例方法 ContainingType::methodName
引用構(gòu)造方法 ClassName::new

我們由此可以歸納出方法引用的規(guī)律,::操作符后面是方法名,方法名不帶括號(hào),雖說有四種形式,但是大體都是符合Java語法的。比如靜態(tài)方法由類名調(diào)用,實(shí)例方法由實(shí)例化對(duì)象調(diào)用,只是將以前的點(diǎn)號(hào)替換為雙冒號(hào)而已。

同時(shí)要注意,Lambda表達(dá)式左邊的參數(shù)要和所引用的方法的參數(shù)要一致,不能多也不能少,順序也要一致。

方法引用這種語法只有一個(gè)用途,就是用在Lambda表達(dá)式,以增加代碼的簡潔,如官方文檔所說:
In those cases, it's often clearer to refer to the existing method by name. Method references enable you to do this; they are compact, easy-to-read lambda expressions for methods that already have a name.

4.小結(jié)

在Android Studio沒有正式支持Lambda表達(dá)式時(shí),就已經(jīng)可以將我們寫的代碼以Lambda表達(dá)式的形式展現(xiàn),大家就可以感受到Lambda表達(dá)式帶來的便利。

不過Lambda表達(dá)式表達(dá)式雖然非常簡潔,但是對(duì)代碼不熟悉的人,初次看見還是增加了不少閱讀難度(其省略了類名,抽象方法名,參數(shù)類型,甚至改變的參數(shù)名,而Java非常提倡使用一些有清晰語義的命名,這些命名上的提示對(duì)于代碼閱讀和理解會(huì)有很大幫助)。

但是一旦熟練后你的代碼將變得非常整齊簡潔,無論是以后的修改還是別人閱覽都會(huì)提供很多便利。(尤其是在Android開發(fā)中使用RxJava一類的API,其中夾雜著大量匿名內(nèi)部類,Lambda表達(dá)式的引入更是讓本來就很清晰的代碼邏輯更加完美)

Java8學(xué)習(xí)筆記目錄

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容