本文地址:http://www.itdecent.cn/p/dd24738d2b11
寫(xiě)在前面:
Java8已經(jīng)發(fā)布很久了,但是安卓官方對(duì)它的支持一直不是很完整,直到最近發(fā)布的Android Studio 3預(yù)覽版才支持了函數(shù)式接口,集合的聚合操作等,所以現(xiàn)在有必要真正地來(lái)去學(xué)習(xí)它了。
本文主要針對(duì)Android Studio(3.0 canary1)對(duì)java api支持的部分做說(shuō)明,當(dāng)然,這部分的特性也完全是java8的一些特性,是其一個(gè)子集而沒(méi)有其它差異之處。
本來(lái)這篇東西涉及到了java8的好幾個(gè)特性,包括函數(shù)式接口,一些標(biāo)準(zhǔn)的函數(shù)式接口,集合的聚合操作和lambda表達(dá)式,但是一通了解準(zhǔn)備下來(lái),發(fā)現(xiàn)它們都是圍繞著函數(shù)式接口這一個(gè)東西在呈現(xiàn)的,并且內(nèi)容并不復(fù)雜,所以我打算用這一篇把這幾個(gè)特性都講了吧。當(dāng)然,我這里只是挑最重要最核心最能讓大家明白的幾點(diǎn)來(lái)講,需要更詳細(xì)了解更多用法和細(xì)節(jié)的可以參看文末的官方文檔地址。
既然說(shuō)是圍繞函數(shù)式接口來(lái)呈現(xiàn)的,那我們就先從函數(shù)式接口開(kāi)始說(shuō)起吧。
函數(shù)式接口
定義:
只有一個(gè)抽象方法的接口
( 當(dāng)然,這是我的白話定義,刪繁就簡(jiǎn)我們就不啰嗦它的那些default方法和靜態(tài)方法了。這兩個(gè)特性完全只是對(duì)接口的一些能力擴(kuò)展,不必大驚小怪,所以也是為什么我覺(jué)得不需要另外一篇文章介紹這些特性。)
這個(gè)定義就是專門(mén)為了lambda表達(dá)式的應(yīng)用而設(shè)計(jì)的。只要理解到這里就可以了,事實(shí)也就這些吧。它并沒(méi)有新的東西。倒是api里創(chuàng)建了一堆標(biāo)準(zhǔn)的函數(shù)式接口給各位使用,就是下面講的這個(gè):
標(biāo)準(zhǔn)函數(shù)式接口
java8硬是要提供這么一個(gè)“工具類”(嗯,因?yàn)樗旁诹薺ava.util.function包下,我們確實(shí)完全可以理解成是一個(gè)工具),我想也是極力引導(dǎo)大家運(yùn)用依賴倒置的原則吧,啥擴(kuò)展代碼都不要寫(xiě)進(jìn)原有方法里面,只使用這些標(biāo)準(zhǔn)接口對(duì)接相應(yīng)的輸入輸出類型,原有方法就只調(diào)用這些接口……但這是不是想多了???我們需要擴(kuò)展的時(shí)候,自然會(huì)自己定義一個(gè)合理的(起碼名字更好理解的)接口來(lái)解耦代碼,真需要你提供這么一堆?但既然提供了,我們也只好試著將就著勉強(qiáng)著用用先吧,起碼在輸入輸出類型一致時(shí),我們不需要去定義多個(gè)功能相同的接口吧(可能設(shè)計(jì)者就只是這么想呢。。。確實(shí),就是為了省點(diǎn)代碼?作為jdk,這是不是管得太寬想得太多了?這是不是過(guò)度設(shè)計(jì)了?為了省點(diǎn)代碼,讓api變累贅?成功與否,看大家接受程度如何吧!記憶這些接口是一回事,我個(gè)人是推崇更自由地編寫(xiě)代碼的,包括思想上的自由,不是由你來(lái)給我設(shè)計(jì)接口,當(dāng)然如果自己不會(huì)設(shè)計(jì)接口,那是水平問(wèn)題,這門(mén)檻也不高,不應(yīng)該由jdk來(lái)做約束,限制了會(huì)設(shè)計(jì)接口的人的自由?!@有點(diǎn)像搖號(hào),看似解決問(wèn)題,但卻傷害著所有人的根本利益。當(dāng)然,你會(huì)說(shuō)你也還可以自己定義接口啊。但這不一樣,你既然提供了一個(gè)這樣的東西,你對(duì)整個(gè)環(huán)境的影響是在的,我定義接口時(shí)就不得不考慮那些使用這些標(biāo)準(zhǔn)接口的人,甚至說(shuō)不定有些團(tuán)隊(duì)還會(huì)因此而將標(biāo)準(zhǔn)接口列入編碼規(guī)范,這些影響都是客觀的,事物都是相互聯(lián)系相互影響的。扯遠(yuǎn)了,不過(guò)也可以幫助大家理解這個(gè)函數(shù)式接口吧)
java8內(nèi)置的幾個(gè)標(biāo)準(zhǔn)函數(shù)式接口:
Predicate
boolean test(T)
表示判斷。輸入一個(gè)對(duì)象,返回布爾值
Consumer
void accept(T)
消費(fèi)一個(gè)操作。輸入一個(gè)對(duì)象,處理(消費(fèi))完后不返回值
Supplier
T get()
直接返回一個(gè)對(duì)象
Function
R apply(T)
就是函數(shù)映射的意思,一個(gè)輸入,一個(gè)相應(yīng)的輸出
主要是以上4種接口了,另外還有很多都是在這幾種的基礎(chǔ)之上的擴(kuò)展,比如加前綴Bi的表示輸入兩個(gè)參數(shù),加了類型Int的表示返回的類型用int類型等等。
又一個(gè)題外話——
這樣只需要在使用的時(shí)候?qū)憣?shí)現(xiàn), 類不需要再變。但省去了很多信息,是否真的有必要這樣?更何況也不是所有函數(shù)都需要擴(kuò)展,這樣是不是太過(guò)了?原有的類是不變了,但擴(kuò)展時(shí)時(shí)都要變呢。
Lambda表達(dá)式
作用
在方法中只有一條語(yǔ)句時(shí),省略更多代碼。
語(yǔ)法
1.包括一個(gè)箭頭->
2.箭頭左邊是參數(shù),可以各種省略類型和括號(hào)
3.箭頭右邊是函數(shù)體,可以各種省略花括號(hào)和關(guān)鍵字return
使用場(chǎng)景
實(shí)際上,lambda表達(dá)式只能在函數(shù)式接口上使用。 就是為了在只有一個(gè)方法時(shí)省點(diǎn)代碼。
另外,因?yàn)樗雌饋?lái)很像一個(gè)函數(shù),所以你可以當(dāng)它是一個(gè)匿名方法----沒(méi)有名字的方法 (來(lái)自于官方網(wǎng)址的說(shuō)明哦)
下面,舉例說(shuō)明一下lambda表達(dá)式在各種具體場(chǎng)景中的運(yùn)用吧:
- 函數(shù)式接口中:
setOnClickListener(view->view.setAlpha(255));
相當(dāng)于
setOnClickListener(new OnClickListener(){
public void onClick(View view){
view.setAlpha(255);
}
})
標(biāo)準(zhǔn)函數(shù)式接口也一樣,只不過(guò)接口是由jdk提供的(android里由安卓sdk提供),具體可參看文末提供的官網(wǎng)鏈接里的例子。由于代碼量較多就不貼了。
還有個(gè)泛型的應(yīng)用:
public static <X, Y> void processElements(
Iterable<X> source,
Predicate<X> tester,
Function <X, Y> mapper,
Consumer<Y> block) {
for (X p : source) {
if (tester.test(p)) {
Y data = mapper.apply(p);
block.accept(data);
}
}
}
調(diào)用時(shí):
processElements(
new ArrayList<Person>(3),
p -> p.getAge() <= 25,
p -> p.getAge(),
age -> System.out.println(age)
);
這個(gè)泛型要注意的是,它的類型由最先使用到它的那個(gè)地方來(lái)確定,比如Y類型,就是由最先使用到它的mapper返回getAge時(shí)來(lái)確定是int類型的,在block輸入Y時(shí)就是個(gè)int類型,也就是在打印時(shí)age是個(gè)int類型。
- 另外,順帶講一下集合的聚合操作
java8對(duì)響應(yīng)式編程的這一點(diǎn)支持是最值得稱贊的,這一塊需要另外講一篇。我們還是先講回lambda表達(dá)式。
其實(shí)這個(gè)跟lambda沒(méi)有什么必然聯(lián)系,聚合操作是java8為集合提供的一個(gè)方便的流式寫(xiě)法。只不過(guò)由于其中的參數(shù)都剛好是函數(shù)式接口,所以也可以用lambda來(lái)表示而已。它也可以不用lambda來(lái)表示,絲毫不影響這個(gè)聚合操作的優(yōu)點(diǎn)發(fā)光發(fā)熱。
new ArrayList<Person>()
.stream()
.filter(p -> p.getAge() >= 18 && p.getAge() <= 25)
.map(p -> p.getAge())//相當(dāng)于Function接口,map在這里是(函數(shù))映射的意思
.forEach(age -> System.out.println(age));
Lambda表達(dá)式變量的作用域
這一點(diǎn)的最后,還要提一下變量在lambda表達(dá)式中的作用域(這才是最實(shí)在的語(yǔ)法糖)
1.lambda函數(shù)體里可直接訪問(wèn)方法傳遞過(guò)來(lái)的參數(shù),但此時(shí)相當(dāng)于認(rèn)為該參數(shù)是final型的,不允許再次賦值
2.lambda函數(shù)體里的this指的是函數(shù)外面最近一層類的實(shí)例,不是指函數(shù)所代表的接口實(shí)例
方法引用
方法引用是在lambda表達(dá)式中特定情形下,使用的特定表示語(yǔ)法。
具體限定為:當(dāng)傳過(guò)來(lái)的參數(shù)列表跟要調(diào)用的方法的參數(shù)能對(duì)應(yīng)上,并且lambda函數(shù)體內(nèi)只有一個(gè)方法調(diào)用語(yǔ)句時(shí),如果符合以下四種情形之一,可以使用方法引用的語(yǔ)法(使用雙冒號(hào)::)來(lái)表示。
1.靜態(tài)方法
ContainingClass::staticMethodName
2.實(shí)例方法
containingObject::instanceMethodName
3.特定類型的實(shí)例方法
ContainingType::methodName
4.構(gòu)造器的使用
ClassName::new
比如:
Arrays.sort(rosterAsArray, Person::compareByAge);
相當(dāng)于:
Arrays.sort(rosterAsArray,
(a, b) -> Person.compareByAge(a, b)
);
這是靜態(tài)方法類型的使用,其它類型可參見(jiàn)官網(wǎng)相關(guān)章節(jié)。
需要注意的是,它只能在代替lambda表達(dá)式的時(shí)候使用。也就限定了只能在函數(shù)式接口上使用了,不能單獨(dú)當(dāng)作一句普通表達(dá)式來(lái)用的。比如,單獨(dú)寫(xiě)這么一句:
Person::compareByAge;//這是不行的,哪怕那個(gè)方法是個(gè)不需要參數(shù)的也不行。
官方文檔地址:
https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html相關(guān)文章:
http://blog.csdn.net/qq_28899635/article/details/53691986