Android 中l(wèi)ambda的小使用

前言

Lambda表達(dá)式是在JDK 8中開(kāi)始支持的一種函數(shù)式推導(dǎo)語(yǔ)言,能夠大量減少匿名內(nèi)部類(lèi)那種冗余的代碼。

任務(wù)

明白怎樣簡(jiǎn)單的使用和看懂lambda。

AS配置使用環(huán)境

要想使用lambda最起碼需要Studio支持吧,我使用的是**Studio 2.3.3 **,因?yàn)樵?.1.1之后支持lambda插件,所以我的操作如下:

 android {
      defaultConfig {
          jackOptions {
              // 打開(kāi)jack編譯器
              enabled true
          }
      }

      // 編譯支持Java8
      compileOptions {
          sourceCompatibility JavaVersion.VERSION_1_8
          targetCompatibility JavaVersion.VERSION_1_8
      }
  }

最開(kāi)始沒(méi)有添加下列代碼:

          jackOptions {
              // 打開(kāi)jack編譯器
              enabled true
          }

報(bào)錯(cuò):Error:Jack is required to support java 8 language features. Either enable Jack or remove sourceCompatibility JavaVersion.VERSION_1_8.
當(dāng)時(shí)一臉懵逼的樣子;

擴(kuò)展:
  • 2016 年 3 月 10 日, Google 向外界發(fā)布了 Android N 的預(yù)覽版,并宣布了 Android N 的 Roadmap ,Android N 的最終版源代碼將于今年 8 或 9 月份釋出到 AOSP 項(xiàng)目。
  • 在眾多的 Android N 新特性中,有一項(xiàng)新工具鏈的出現(xiàn)與 Android 生態(tài)圈的所有開(kāi)發(fā)者息息相關(guān),即 Jack & Jill 編譯器的引入。在依賴了 Sun/Oracle 的 Java 編譯器十年之后,Jack 是 Java Android Compiler Kit 的縮寫(xiě),它可以將 Java 代碼直接編譯為 Dalvik 字節(jié)碼,并負(fù)責(zé) Minification, Obfuscation, Repackaging, Multidexing, Incremental compilation。它試圖取代 javac/dx/proguard/jarjar/multidex 庫(kù)等工具。
  • Android 終于有了自己的 Java 編譯器。Android7.0(API24)在對(duì)JAVA8 語(yǔ)言功能的支持上,需要一個(gè)名為 Jack 的新編譯。Jack 僅在 Android Studio 2.1 和更高版本上才受支持。因此,如果要使用 Java 8 語(yǔ)言功能,則需使用 Android Studio 2.1 開(kāi)發(fā)應(yīng)用。
  • 支持 Java 8 語(yǔ)言功能需要一個(gè)名為 Jack 的新編譯。Jack 僅在 Android Studio 2.1 和更高版本上才受支持。因此,如果要使用 Java 8 語(yǔ)言功能,則需使用 Android Studio 2.1 開(kāi)發(fā)應(yīng)用,還需使用新的 Jack 工具鏈。新的 Android 工具鏈將 Java 源語(yǔ)言編譯成 Android 可讀取的 Dalvik 可執(zhí)行文件字節(jié)碼,且有其自己的 .jack庫(kù)格式,在一個(gè)工具中提供了大多數(shù)工具鏈功能:重新打包、壓縮、模糊化以及 Dalvik 可執(zhí)行文件分包。

以下是構(gòu)建 Android Dalvik 可執(zhí)行文件可用的兩種工具鏈的對(duì)比:

  • 舊版 javac 工具鏈:
    javac (.java –> .class) –> dx (.class –> .dex)
  • 新版 Jack 工具鏈:
    Jack (.java –> .jack –> .dex)

有小伙伴說(shuō)了:我的不是2.1.1之前的怎么辦???不要急,代碼如下

  1. 在 Project 的 build.gradle 中添加如下代碼
  dependencies {
        classpath 'me.tatarka:gradle-retrolambda:3.2.0'
  }
  1. 在 Module 的 build.gradle 中添加如下代碼
 // 應(yīng)用插件
 apply plugin: 'me.tatarka.retrolambda'
 // 支持Java8
 android {
     compileOptions {
         sourceCompatibility JavaVersion.VERSION_1_8
         targetCompatibility JavaVersion.VERSION_1_8
     }
 }

配置完,編譯一下就大功告成
只可惜沒(méi)有測(cè)試,有心的小伙伴可以測(cè)試一下。

使用Lambda

lambda 表達(dá)式共有三種形式:函數(shù)式接口、方法引用和構(gòu)造器引用。
函數(shù)式接口:是指有且只有一個(gè)抽象方法的接口,比如各種Listener接口和Runnable接口。lambda表達(dá)式就是對(duì)這類(lèi)接口的匿名類(lèi)進(jìn)行簡(jiǎn)化。基本形式如下:
(參數(shù)列表...) ->{語(yǔ)句塊...}
具體語(yǔ)法:

語(yǔ)法一:()->{}
其中()和{}可以看情況去掉,()只有一個(gè)參數(shù)可去掉,{}里邏輯只有一行可去掉
詳情如下:

  • 無(wú)參數(shù)時(shí),直接這么寫(xiě)
    // 創(chuàng)建Runnable對(duì)象
    Runnable runnable = () -> {

     };
  • 有一個(gè)參數(shù),直接參數(shù)名 ->{}
  View.OnClickListener listener = view -> {

  };
  • 有多個(gè)參數(shù),只需要在()里寫(xiě)上參數(shù)名字就可以
View.OnFocusChangeListener listener = (view, b) -> {

  };

舉例子:上一個(gè)簡(jiǎn)單的代碼:

     //定義一個(gè)接口
    interface Comparator<T> {
        int compare(T var1, T var2);
    }
    
    //老的實(shí)現(xiàn)方法
    Comparator comparator = new Comparator<String>() {
        @Override
        public int compare(String var1, String var2) {
            return 0;
        }
    };
    
    //lambda優(yōu)化一下
    Comparator<String> comparator1 = (String s1, String s2) -> {
        return 0;
    };
    
    //lambda最后的優(yōu)化,當(dāng)編譯器可以推導(dǎo)出具體的參數(shù)類(lèi)型時(shí)
    Comparator<String> comparator2 = (s1, s2) -> {
        return 0;
    };
  • 當(dāng)語(yǔ)句塊內(nèi)的處理邏輯只有一句表達(dá)式時(shí),其兩側(cè)的花括號(hào)也可省略,特別注意這句處理邏輯表達(dá)式后面也不帶分號(hào)。
button.setOnClickListener(view1 -> activity.finish());

同時(shí),當(dāng)只有一句去除花括號(hào)的表達(dá)式且接口方法需要返回值時(shí),這個(gè)表達(dá)式不用(也不能)在表達(dá)式前加 return ,就可以當(dāng)作返回語(yǔ)句。下面用 Java 的 Function 接口作為示例,這是一個(gè)用于轉(zhuǎn)換類(lèi)型的接口,在這里我們自己造一個(gè)使用:

    interface Function<T, R> {
        R applay(T t);
    }
    //原來(lái)的寫(xiě)法
    Function<Integer, String> function = new Function<Integer, String>() {
        @Override
        public String applay(Integer integer) {
            return String.valueOf(integer);
        }
    };
    //lambda的寫(xiě)法
    Function<Integer, String> function1 = integer -> String.valueOf(integer);

方法引用:就是當(dāng)邏輯實(shí)現(xiàn)只有一句且調(diào)用了已存在的方法進(jìn)行處理( this 和 super 的方法也可包括在內(nèi))時(shí),對(duì)函數(shù)式接口形式的 lambda 表達(dá)式進(jìn)行進(jìn)一步的簡(jiǎn)化。傳入引用方法的參數(shù)就是原接口方法的參數(shù)。
接下來(lái)總結(jié)一下方法引用形式的三種格式:

  • object :: instanceMethod

直接調(diào)用任意對(duì)象的實(shí)例方法,如 obj::equals 代表調(diào)用 obj 的 equals 方法與接口方法參數(shù)比較是否相等,效果等同 obj.equals(t);。

當(dāng)前類(lèi)的方法可用this::method進(jìn)行調(diào)用,父類(lèi)方法同理。

  • ClassName :: staticMethod

直接調(diào)用某類(lèi)的靜態(tài)方法,并將接口方法參數(shù)傳入,如上述 TextUtils::isEmpty,效果等同 TextUtils.isEmpty(s);
例子如下:

  Predicate<String> predicate = new Predicate<String>() {
        @Override
        public boolean test(String s) {
            // 下面的代碼和TextUtils.isEmpty(s)等價(jià)。
            return s == null || s.length() == 0;
        }
    };
    //進(jìn)一步用lambda簡(jiǎn)化得到
    Predicate<String> predicate1 = s -> TextUtils.isEmpty(s);
    //但是還是可以繼續(xù)簡(jiǎn)化的,我的天?。。。?    Predicate<String> predicate2 = String::isEmpty;
  • ClassName :: instanceMethod

較為特殊,將接口方法參數(shù)列表的第一個(gè)參數(shù)作為方法調(diào)用者,其余參數(shù)作為方法參數(shù)。由于此類(lèi)接口較少,故選擇 Java 提供的 BiFunction 接口作為示例,該接口方法接收一個(gè) T1 類(lèi)對(duì)象和一個(gè) T2 類(lèi)對(duì)象,通過(guò)處理后返回 R 類(lèi)對(duì)象:

    interface BitFunction<T1, T2, R> {
        R applay(T1 t1, T2 t2);
    }
    BitFunction<String, String, Boolean> bitFunction = new BitFunction<String, String, Boolean>() {
        @Override
        public Boolean applay(String s, String s2) {
            return s.endsWith(s2);
        }
    };
    //使用lambda得到下面的結(jié)果
    BitFunction<String, String, Boolean> bitFunction1 = String::endsWith;
    //ClassName 為接口方法的第一個(gè)參數(shù)的類(lèi)名,同時(shí)利用接口方法的第一個(gè)參數(shù)作為方法調(diào)用者,其余參數(shù)作為方法參數(shù),實(shí)現(xiàn)s1.endsWith(s2);
    //補(bǔ)充一個(gè)例子,
    BitFunction<List<String>,List<String>,Boolean> bitFunction3=List ::contains;

構(gòu)造器引用
Lambda 表達(dá)式的第三種形式,其實(shí)和方法引用十分相似,只不過(guò)方法名替換為 new 。其格式為 ClassName :: new。這時(shí)編譯器會(huì)通過(guò)上下文判斷傳入的參數(shù)的類(lèi)型、順序、數(shù)量等,來(lái)調(diào)用適合的構(gòu)造器,返回對(duì)象。

使用技巧

Android Studio 會(huì)在可以轉(zhuǎn)化為 lambda 表達(dá)式的代碼上進(jìn)行如圖的灰色標(biāo)識(shí),這時(shí)將光標(biāo)移至灰色區(qū)域,按下 Alt + Enter ,選擇第一項(xiàng)(方法引用和構(gòu)造器引用在第二項(xiàng)),IDE 就會(huì)自動(dòng)進(jìn)行轉(zhuǎn)換。還有就是在代碼進(jìn)行編譯的時(shí)候比如你要新建一個(gè)對(duì)象的時(shí)候也可以按下 Alt + Enter,就會(huì)得到你想要的結(jié)果。如下圖所示:

img.png

特別注意

this 關(guān)鍵字
在匿名內(nèi)部類(lèi)中,this 關(guān)鍵字指向的是匿名類(lèi)本身的對(duì)象,而在 lambda 中,this 指向的是 lambda 表達(dá)式的外部類(lèi)。

方法數(shù)量差異
當(dāng)前 Android Studio 對(duì) Java 8 新特性編譯時(shí)采用脫糖(desugar)處理,lambda 表達(dá)式經(jīng)過(guò)編譯器編譯后,每一個(gè) lambda 表達(dá)式都會(huì)增加 1~2 個(gè)方法數(shù)。而 Android 應(yīng)用的方法數(shù)不能超過(guò) 65536 個(gè)。雖然一般應(yīng)用較難觸發(fā),但仍需注意。

我會(huì)不定時(shí)的對(duì)文章進(jìn)行補(bǔ)充的哦!??!加油吧騷年。。。
最后編輯于
?著作權(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),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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