今天google官方放出了期待已久的AS3.0正式版,加入了很多新特性碴萧,支持kotlin,java8末购,編譯速度更快等等破喻,想必很多kotlin小迷弟,小迷妹們已經(jīng)歡呼雀躍盟榴。然鵝曹质,菜雞如我,不會(huì)使用kotlin擎场。只能研究一下作為android開(kāi)發(fā)者在AS3.0中可以使用到的java8新特性羽德。
官方對(duì)于AS中J8新特性的頁(yè)面如下Use Java 8 Language Features(請(qǐng)自備紅杏)
配置Java8
首先需要拋棄之前的第三方插件
Android Studio為使用某些Java 8語(yǔ)言功能和使用它們的第三方庫(kù)提供內(nèi)置支持。迅办,默認(rèn)工具鏈通過(guò)desugar在javac編譯器的輸出上執(zhí)行字節(jié)碼轉(zhuǎn)換來(lái)實(shí)現(xiàn)新的語(yǔ)言特性宅静。
在3.0之前,相信很多小伙伴為了嘗試java8的新特性站欺,會(huì)使用第三方的插件來(lái)使用lambda姨夹,如:Jack, Retrolambda, 或 DexGuard,AS3.0將對(duì)其不再提供支持矾策,如果as檢測(cè)到你繼續(xù)使用這些插件磷账,as將繼續(xù)使用第三方插件,而不使用默認(rèn)的工具鏈蝴韭。
刪除Jack支持
將jackOptions
從build.gradle
刪除
android {
...
defaultConfig {
...
// 刪除此代碼塊
jackOptions {
enabled true
...
}
}
刪除Retrolambda
請(qǐng)從項(xiàng)目級(jí)build.gradle
文件中刪除Retrolambda
依賴關(guān)系
buildscript {
...
dependencies {
// 刪除此依賴
classpath 'me.tatarka:gradle-retrolambda:<version_number>'
}
}
并刪除每個(gè)moudle下的build.gradle
文件中Retrolambda
引入
//刪除
apply plugin: 'me.tatarka.retrolambda'
//刪除(如果有的話)
retrolambda {
jvmArgs '-Xmx2048m'
}
設(shè)置Java8
在build.gradle
文件下android
節(jié)點(diǎn)添加如下
android {
...
//將項(xiàng)目的源和目標(biāo)兼容性值設(shè)置為Java 8
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
AS中的Java8新特性
放一張官方的資料圖:
可以看到新版AS中指支持部分Java8的新特性够颠,且有一部分還有minSdkVersion
限制。接下來(lái)榄鉴,我們對(duì)其比較常用的進(jìn)行講解:
Lambda
相信大家已經(jīng)lambda已經(jīng)非常熟悉履磨,這里我們不講語(yǔ)法,講點(diǎn)別的需要注意的地方庆尘。
與匿名內(nèi)部類的區(qū)別
lambda大家看作是一種匿名內(nèi)部類剃诅,但實(shí)際卻與匿名內(nèi)部類是有區(qū)別的,
-
lambda
編譯完成之后驶忌,使用的是J7新增的指令invokedynamic
來(lái)表示矛辕,具體的實(shí)現(xiàn)不體現(xiàn)在字節(jié)碼文件中笑跛,由虛擬機(jī)進(jìn)行翻譯lambda
的具體執(zhí)行策略,以便于后續(xù)JDK版本優(yōu)化此語(yǔ)法特性的策略聊品,同時(shí)可以很大程度上減少字節(jié)碼的文件的大小和數(shù)量飞蹂,分配方式類似于java.lang.invoke
中MethodHandle
中的動(dòng)態(tài)調(diào)用方法(具體請(qǐng)百度)。目前lambda
的具體執(zhí)行方式還是與匿名內(nèi)部類差不多的策略翻屈,據(jù)說(shuō)oracle在以后的版本更新中會(huì)優(yōu)化分配測(cè)羅陈哑,選用MethodHandle
。 - 而匿名內(nèi)部類方式生成新類伸眶,新建一個(gè)標(biāo)有
$xx.class
的文件惊窖。
分類
lambda
表達(dá)式的類型是一種特殊的接口,這種接口只有一個(gè)抽象方法厘贼,成為函數(shù)接口界酒。
在J8中,lambda表達(dá)式傾向于函數(shù)式編程嘴秸,根據(jù)其輸入與返回類型不同毁欣,分為幾種類型的函數(shù)接口,下面是系統(tǒng)提供的一組函數(shù)式接口類型赁遗,并以Rxjava的操作符舉例:
接口 | 參數(shù) | 返回類型 | 說(shuō)明 |
---|---|---|---|
Predicate<T> | T | boolean | 判斷型函數(shù)署辉,filter |
Consumer<T> | T | void | 消費(fèi)型函數(shù)族铆,subcribe |
Function<T,R> | T | R | 轉(zhuǎn)換型函數(shù)岩四,map |
Supplier<T> | None | T | 生產(chǎn)型函數(shù),create |
UnaryOperator<T> | T | T | Function的特殊情況 |
BinaryOperator<T> | (T,T) | T | (二元)多元運(yùn)算哥攘,zip |
我們可以同過(guò)自定義接口進(jìn)行使用剖煌,如android的View.OnclickListener極為消費(fèi)性函數(shù)。
注意莉掂,在自定義函數(shù)接口時(shí)胳岂,站在用途的角度來(lái)考慮蚓耽,為了區(qū)分普通接口與函數(shù)接口的區(qū)別,建議自定的函數(shù)接口都應(yīng)該加上
@FunctionalInterface
注解茉兰。該直接會(huì)強(qiáng)制編譯器檢查一個(gè)接口是否符合函數(shù)接口的標(biāo)準(zhǔn)。如果該注釋添加給一個(gè)枚舉類型欣簇,類或者接口包含不止一個(gè)抽象方法规脸,編譯器就會(huì)報(bào)錯(cuò)。這對(duì)我們規(guī)范化代碼有很大的幫助熊咽。
重載解析
莫鸭,在運(yùn)行過(guò)程中,會(huì)根據(jù)横殴,java中可以重載方法被因,造成多個(gè)方法會(huì)有相同的方法名,但簽名卻不一樣。這在推斷類型是會(huì)帶來(lái)問(wèn)題梨与,因?yàn)橄到y(tǒng)可能會(huì)推斷出多種方法類型堕花。當(dāng)lambda表達(dá)式作為參數(shù)時(shí),當(dāng)出現(xiàn)重載情況時(shí)粥鞋,這時(shí)航徙,
java會(huì)挑出最具體的類型。
看一個(gè)例子:
static interface IntergerBifunciton extends BinaryOperator<Integer>{
}
public void overloadedMethoe(BinaryOperator<Integer> l){
}
public void overloadedMethoe(IntergerBifunciton l){
}
接口IntergerBifunciton
是BinaryOperator
的子接口陷虎,在調(diào)用overloadedMethoe((x,y)->x+y)
的時(shí)候到踏,虛擬機(jī)會(huì)選用最具體的類型
。
再看一個(gè)無(wú)法推斷哪個(gè)更具體類型的例子:
static interface Intergerfunciton{
public boolean test(int x);
}
public void overloadedMethoe(Predicate<Integer> l){
}
public void overloadedMethoe(Intergerfunciton l){
}
我們使用overloadedMethoe((x)->true);
調(diào)用的時(shí)候尚猿,會(huì)出現(xiàn)錯(cuò)誤:
這是因?yàn)槲迅澹幾g器無(wú)法推斷出哪種類型更具體 更適合方法的參數(shù),故報(bào)錯(cuò)凿掂。我們?cè)谌粘i_(kāi)發(fā)中應(yīng)該避免出現(xiàn)這種情況伴榔。
Default and static interface methods(默認(rèn)方法和靜態(tài)接口方法)
- 默認(rèn)方法
java8相比之前最大的改動(dòng)無(wú)疑是對(duì)集合類的改動(dòng)和添加Stream
API(minSdkVersion為24可用),用于在集合類中更方便的進(jìn)行函數(shù)是編程庄萎,大家可以把Stream
API看成類似Rxjava
的類庫(kù)踪少,用以方便的對(duì)數(shù)據(jù)進(jìn)行變換。(同樣的糠涛,Stream
也有很多類似Rxjava
操作符的方法援奢,比如filter,map忍捡,toList集漾,flatmap等等,使用方法大同小異)砸脊。
java為Collection
接口添加了stream
方法(見(jiàn)下圖)具篇,但是問(wèn)題出現(xiàn)了,這意味著java1-java7中凌埂,實(shí)現(xiàn)了Collection
接口的全部類都需要實(shí)現(xiàn)stream
方法驱显,否則,那些類將無(wú)法在java8里面通過(guò)編譯瞳抓。java為了保持向下兼容性埃疫,java8引入了默認(rèn)方法
,用關(guān)鍵字default
表示挨下。形象化說(shuō)法就是:Collection
告訴他的子類熔恢,如果你沒(méi)有實(shí)現(xiàn)stream
方法,就用我的吧臭笆。
我們可以看到叙淌,在j8中秤掌,很多地方都使用了默認(rèn)方法,如Iterable
接口中的forEach
和spliterator
鹰霍。
子接口中或者實(shí)現(xiàn)類中實(shí)現(xiàn)覆蓋默認(rèn)方法闻鉴,需要Override
注解。
看一下默認(rèn)方法在復(fù)雜的繼承關(guān)系中的一種特殊情況:
如圖茂洒,OverrideChild
既實(shí)現(xiàn)了Parent
接口孟岛,又繼承了實(shí)現(xiàn)了Parent
接口的類,那么調(diào)用時(shí)督勺,應(yīng)該調(diào)用哪個(gè)方法呢渠羞?此例子打印出來(lái)的是from ParentImpl
,原因在于,與接口中定義的默認(rèn)方法相比智哀,類中重寫(xiě)的方法更具體
次询。
調(diào)用優(yōu)先級(jí)為:類重寫(xiě)方法>子接口重寫(xiě)方法>父接口方法
接口允許多繼承,因此瓷叫,有了默認(rèn)方法屯吊,子類就可以繼承多個(gè)接口中的默認(rèn)實(shí)現(xiàn)。這在某種形式上實(shí)現(xiàn)了多重繼承的功能摹菠。(注意:如果在繼承的多個(gè)接口中盒卸,有多個(gè)簽名相同的默認(rèn)方法,編譯器將報(bào)錯(cuò))
- 接口的靜態(tài)方法
在以往的jdk版本中次氨,接口是不允許存在靜態(tài)方法的蔽介,j8發(fā)布后,為了使Stream.of
靜態(tài)方法糟需,加入了靜態(tài)方法屉佳,使得同一接口的子類可以共享這一靜態(tài)方法谷朝。
try-with-resources
AS3.0在所有android api層面對(duì)try-with-resources
進(jìn)行支持洲押。
try-with-resources聲明一個(gè)或多個(gè)資源的 try 語(yǔ)句。一個(gè)資源作為一個(gè)對(duì)象圆凰,必須在程序結(jié)束之后隨之關(guān)閉杈帐。 try-with-resources語(yǔ)句確保在語(yǔ)句的最后每個(gè)資源都被關(guān)閉 。
任何實(shí)現(xiàn)了 java.lang.AutoCloseable的對(duì)象, 包括所有實(shí)現(xiàn)了 java.io.Closeable 的對(duì)象, 都可以用作一個(gè)資源专钉。
先來(lái)一段代碼:
可以看到挑童, try-with-resources
語(yǔ)句聲明的資源是一個(gè) BufferedReader。聲明語(yǔ)句在緊跟在 try 關(guān)鍵字的括號(hào)里面跃须。在j8之前站叼,如果我要是有一個(gè)資源,在使用之后菇民,需要顯示的效用其close
方法將其關(guān)閉尽楔,但j8新的語(yǔ)法糖出現(xiàn)后投储,我們只需要將其聲明在try中,在資源使用完畢之后阔馋,將自動(dòng)關(guān)閉玛荞,不需要手動(dòng)調(diào)用。上面的語(yǔ)句相當(dāng)于:
我們可以在try后聲明多個(gè)資源呕寝,見(jiàn)下圖:
我們也可以在try-with-resources
語(yǔ)句后添加自己需要捕獲的異逞校或添加finally
語(yǔ)句塊,如下圖:
注意下梢,在try-with-resources 語(yǔ)句中, 任意的 catch 或者 finally 塊都是在聲明的資源被關(guān)閉以后才運(yùn)行客蹋。
如果try-with-resources語(yǔ)句中資源的Close方法和try代碼塊中都拋出了異常,Close 方法拋出的異常被抑制孽江,try代碼塊中的異常會(huì)被拋出嚼酝。
Java7之后,可以使用Throwable.getSuppressed方法獲得被抑制的異常竟坛。