我也說(shuō)說(shuō)java8的新特性

本文地址: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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Java8 in action 沒(méi)有共享的可變數(shù)據(jù),將方法和函數(shù)即代碼傳遞給其他方法的能力就是我們平常所說(shuō)的函數(shù)式...
    鐵牛很鐵閱讀 1,357評(píng)論 1 2
  • 簡(jiǎn)介 概念 Lambda 表達(dá)式可以理解為簡(jiǎn)潔地表示可傳遞的匿名函數(shù)的一種方式:它沒(méi)有名稱,但它有參數(shù)列表、函數(shù)主...
    劉滌生閱讀 3,335評(píng)論 5 18
  • 在C++11中,我們還是會(huì)看到一些新元素。這些新鮮出爐的元素可能會(huì)帶來(lái)一些習(xí)慣上的改變,不過(guò)權(quán)衡之下,可能這樣的改...
    認(rèn)真學(xué)計(jì)算機(jī)閱讀 5,631評(píng)論 1 27
  • lambda表達(dá)式(又被成為“閉包”或“匿名方法”)方法引用和構(gòu)造方法引用擴(kuò)展的目標(biāo)類型和類型推導(dǎo)接口中的默認(rèn)方法...
    183207efd207閱讀 1,547評(píng)論 0 5
  • 注:之前關(guān)于Java8的認(rèn)知一直停留在知道有哪些修改和新的API上,對(duì)Lambda的認(rèn)識(shí)也是僅僅限于對(duì)匿名內(nèi)部類的...
    mualex閱讀 2,935評(píng)論 1 4

友情鏈接更多精彩內(nèi)容