本文地址:http://www.reibang.com/p/dd24738d2b11
寫在前面:
Java8已經(jīng)發(fā)布很久了杠人,但是安卓官方對(duì)它的支持一直不是很完整砰粹,直到最近發(fā)布的Android Studio 3預(yù)覽版才支持了函數(shù)式接口,集合的聚合操作等咙好,所以現(xiàn)在有必要真正地來去學(xué)習(xí)它了篡腌。
本文主要針對(duì)Android Studio(3.0 canary1)對(duì)java api支持的部分做說明,當(dāng)然勾效,這部分的特性也完全是java8的一些特性嘹悼,是其一個(gè)子集而沒有其它差異之處叛甫。
本來這篇東西涉及到了java8的好幾個(gè)特性,包括函數(shù)式接口杨伙,一些標(biāo)準(zhǔn)的函數(shù)式接口其监,集合的聚合操作和lambda表達(dá)式,但是一通了解準(zhǔn)備下來限匣,發(fā)現(xiàn)它們都是圍繞著函數(shù)式接口這一個(gè)東西在呈現(xiàn)的抖苦,并且內(nèi)容并不復(fù)雜,所以我打算用這一篇把這幾個(gè)特性都講了吧膛腐。當(dāng)然睛约,我這里只是挑最重要最核心最能讓大家明白的幾點(diǎn)來講,需要更詳細(xì)了解更多用法和細(xì)節(jié)的可以參看文末的官方文檔地址哲身。
既然說是圍繞函數(shù)式接口來呈現(xiàn)的辩涝,那我們就先從函數(shù)式接口開始說起吧。
函數(shù)式接口
定義:
只有一個(gè)抽象方法的接口
( 當(dāng)然勘天,這是我的白話定義怔揩,刪繁就簡(jiǎn)我們就不啰嗦它的那些default方法和靜態(tài)方法了。這兩個(gè)特性完全只是對(duì)接口的一些能力擴(kuò)展脯丝,不必大驚小怪商膊,所以也是為什么我覺得不需要另外一篇文章介紹這些特性。)
這個(gè)定義就是專門為了lambda表達(dá)式的應(yīng)用而設(shè)計(jì)的宠进。只要理解到這里就可以了晕拆,事實(shí)也就這些吧。它并沒有新的東西材蹬。倒是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ò)展代碼都不要寫進(jìn)原有方法里面闸溃,只使用這些標(biāo)準(zhǔn)接口對(duì)接相應(yīng)的輸入輸出類型整吆,原有方法就只調(diào)用這些接口……但這是不是想多了啊辉川?我們需要擴(kuò)展的時(shí)候表蝙,自然會(huì)自己定義一個(gè)合理的(起碼名字更好理解的)接口來解耦代碼,真需要你提供這么一堆乓旗?但既然提供了府蛇,我們也只好試著將就著勉強(qiáng)著用用先吧,起碼在輸入輸出類型一致時(shí)寸齐,我們不需要去定義多個(gè)功能相同的接口吧(可能設(shè)計(jì)者就只是這么想呢欲诺。。渺鹦。確實(shí)扰法,就是為了省點(diǎn)代碼?作為jdk毅厚,這是不是管得太寬想得太多了塞颁?這是不是過度設(shè)計(jì)了?為了省點(diǎn)代碼吸耿,讓api變累贅祠锣?成功與否,看大家接受程度如何吧咽安!記憶這些接口是一回事伴网,我個(gè)人是推崇更自由地編寫代碼的,包括思想上的自由妆棒,不是由你來給我設(shè)計(jì)接口澡腾,當(dāng)然如果自己不會(huì)設(shè)計(jì)接口,那是水平問題糕珊,這門檻也不高动分,不應(yīng)該由jdk來做約束,限制了會(huì)設(shè)計(jì)接口的人的自由红选±焦——這有點(diǎn)像搖號(hào),看似解決問題喇肋,但卻傷害著所有人的根本利益坟乾。當(dāng)然,你會(huì)說你也還可以自己定義接口啊苟蹈。但這不一樣糊渊,你既然提供了一個(gè)這樣的東西,你對(duì)整個(gè)環(huán)境的影響是在的慧脱,我定義接口時(shí)就不得不考慮那些使用這些標(biāo)準(zhǔn)接口的人渺绒,甚至說不定有些團(tuán)隊(duì)還會(huì)因此而將標(biāo)準(zhǔn)接口列入編碼規(guī)范,這些影響都是客觀的菱鸥,事物都是相互聯(lián)系相互影響的宗兼。扯遠(yuǎn)了,不過也可以幫助大家理解這個(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ò)展伯铣,這樣是不是太過了?原有的類是不變了轮纫,但擴(kuò)展時(shí)時(shí)都要變呢腔寡。
Lambda表達(dá)式
作用
在方法中只有一條語句時(shí),省略更多代碼掌唾。
語法
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)樗雌饋砗芟褚粋€(gè)函數(shù)叽粹,所以你可以當(dāng)它是一個(gè)匿名方法----沒有名字的方法 (來自于官方網(wǎng)址的說明哦)
下面,舉例說明一下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ù)式接口也一樣却舀,只不過接口是由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è)地方來確定,比如Y類型螃诅,就是由最先使用到它的mapper返回getAge時(shí)來確定是int類型的啡氢,在block輸入Y時(shí)就是個(gè)int類型,也就是在打印時(shí)age是個(gè)int類型术裸。
- 另外倘是,順帶講一下集合的聚合操作
java8對(duì)響應(yīng)式編程的這一點(diǎn)支持是最值得稱贊的,這一塊需要另外講一篇袭艺。我們還是先講回lambda表達(dá)式搀崭。
其實(shí)這個(gè)跟lambda沒有什么必然聯(lián)系,聚合操作是java8為集合提供的一個(gè)方便的流式寫法猾编。只不過由于其中的參數(shù)都剛好是函數(shù)式接口瘤睹,所以也可以用lambda來表示而已升敲。它也可以不用lambda來表示,絲毫不影響這個(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í)在的語法糖)
1.lambda函數(shù)體里可直接訪問方法傳遞過來的參數(shù)绸吸,但此時(shí)相當(dāng)于認(rèn)為該參數(shù)是final型的,不允許再次賦值
2.lambda函數(shù)體里的this指的是函數(shù)外面最近一層類的實(shí)例设江,不是指函數(shù)所代表的接口實(shí)例
方法引用
方法引用是在lambda表達(dá)式中特定情形下锦茁,使用的特定表示語法。
具體限定為:當(dāng)傳過來的參數(shù)列表跟要調(diào)用的方法的參數(shù)能對(duì)應(yīng)上叉存,并且lambda函數(shù)體內(nèi)只有一個(gè)方法調(diào)用語句時(shí)码俩,如果符合以下四種情形之一,可以使用方法引用的語法(使用雙冒號(hào)::)來表示歼捏。
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)方法類型的使用稿存,其它類型可參見官網(wǎng)相關(guān)章節(jié)。
需要注意的是瞳秽,它只能在代替lambda表達(dá)式的時(shí)候使用瓣履。也就限定了只能在函數(shù)式接口上使用了,不能單獨(dú)當(dāng)作一句普通表達(dá)式來用的练俐。比如袖迎,單獨(dú)寫這么一句:
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