前言
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之前的怎么辦???不要急,代碼如下
- 在 Project 的 build.gradle 中添加如下代碼
dependencies {
classpath 'me.tatarka:gradle-retrolambda:3.2.0'
}
- 在 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é)果。如下圖所示:

特別注意
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ā),但仍需注意。