前言
在Kotlin流行的當(dāng)下,談?wù)?Java 8 或許有點(diǎn)逆潮流。然現(xiàn)代編程語(yǔ)言發(fā)展過(guò)程中有一些共性的東西,談?wù)効赡芤灿兄谙嗷ソ梃b。
Android Gradle 插件 4.0.0 以前,Android 對(duì) Java 8 的支持并不好,對(duì)于函數(shù)式密切相關(guān)的Stream 的支持受限于平臺(tái)版本,使得對(duì)Java 8的使用僅僅停留在 Lambda 表達(dá)式。Gradle 插件 4.0.0 之后,官方通過(guò) desugar 的引入跨越了平臺(tái)版本限制,對(duì) Java 8 有了更多實(shí)際的支持。本文介紹 Android Java 8 基本配置及概念,同時(shí)用一個(gè)例子展示了 Java 8 常用特性。
關(guān)于脫糖(desugar)

如圖所示,Android Java代碼編譯過(guò)程如下
- 通過(guò)JDK編譯生成通用中間碼.class文件
- 通過(guò)R8混淆,D8編譯成可被Android虛擬機(jī)執(zhí)行的Dex文件
在沒(méi)有desugar之前,高版本的.class文件中部分特性字節(jié)碼無(wú)法被D8識(shí)別轉(zhuǎn)化,通過(guò)desugar轉(zhuǎn)化這部分字節(jié)碼。
概念說(shuō)明
- D8:Java后端編譯器,將class文件編譯成Android可執(zhí)行dex文件。
- R8:混淆工具ProGuard代替工具,用于代碼的壓縮(shrinking)和混淆(obfuscation)。
- 脫糖(desuger):集成于D8內(nèi),通過(guò)字節(jié)碼轉(zhuǎn)化支持 Java 高版本語(yǔ)言特性。
開(kāi)啟 Java 8 支持配置
android {
defaultConfig {
// Required when setting minSdkVersion to 20 or lower
multiDexEnabled true
}
compileOptions {
// Flag to enable support for the new language APIs
coreLibraryDesugaringEnabled true
// Sets Java compatibility to Java 8
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.9'
}
常用特性介紹
Lambda 表達(dá)式
Lambda簡(jiǎn)化了單方法接口匿名類的代碼呈現(xiàn)。這些接口,一般具有test、mapper、merge、block等函數(shù)式操作語(yǔ)義。
假設(shè)存在一個(gè)如下數(shù)據(jù)類
class Person {
public int age;
public String name;
public Person() {
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
}
在使用Java 6開(kāi)發(fā)時(shí)候常會(huì)出現(xiàn)如下類似代碼
public List<Person> filter(List<Person> list) {
ArrayList<Person> result = new ArrayList<>();
for (Person person : list) {
if (person.age > 0 && person.name != null) {
result.add(person);
}
}
return result;
}
如果要支持過(guò)濾條件可定義,會(huì)構(gòu)建如下形式代碼
interface Checker {
boolean check(Person person);
}
public List<Person> filter(List<Person> list, Checker checker) {
List<Person> result = new ArrayList<>();
for (Person person : list) {
if (checker.check(person)) {
result.add(person);
}
}
return result;
}
// call place
List<Person> filterPersonList = filter(personList, new Checker() {
@Override
public boolean check(Person person) {
return person.age > 0 && person.name != null;
}
});
使用Lambda替換 call place 位置代碼,結(jié)果如下
// call place
List<Person> filterPersonList = filter(personList, person -> person.age > 0 && person.name != null);
方法引用
Lambda的擴(kuò)展,可以使用本地方法替代Lambda中的箭頭表達(dá)式
對(duì)于上述例子中的call place能進(jìn)一步更改為如下代碼
private boolean localChecker(Person person) {
return person.age > 0 && person.name != null;
}
// call place
List<Person> filterPersonList = filter(personList, this::localChecker);
Stream
Stream對(duì)Collection接口封裝了一系列函數(shù)方法,支持鏈?zhǔn)秸{(diào)用,上例可進(jìn)一步更改為
List<Person> filterPersonList = personList.stream()
.filter(this::localChecker)
.collect(Collectors.toCollection(ArrayList::new));
一般情況下過(guò)濾獲取目標(biāo)不是最終意圖,最終意圖可能是數(shù)據(jù)轉(zhuǎn)換,可能是聚合操作,使用stream能很好的進(jìn)行這些操作
// 聚合
personList.stream()
.filter(this::localChecker)
.count();
// 轉(zhuǎn)換后操作
personList.stream()
.filter(this::localChecker)
.map(p -> p.name)
.sorted();
默認(rèn)和靜態(tài)接口方法
支持接口方法默認(rèn)實(shí)現(xiàn),能規(guī)避新增接口方法時(shí)同時(shí)更改多個(gè)實(shí)現(xiàn)類的情況。源碼中stream實(shí)現(xiàn)即依賴default擴(kuò)展了Collection接口
// Collections.java
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
Optional
Optional用于對(duì)空值處理,相較于Kotlin語(yǔ)法級(jí)別的支持,Optional以類形式的封裝支持略顯不夠優(yōu)雅,當(dāng)相較于通過(guò)規(guī)范約定,注解標(biāo)識(shí)的方式,也算是提供了一個(gè)更不容易出錯(cuò)的方式。