Java8 函數(shù)式編程詳解

Java8 函數(shù)式編程詳解

Author:Dorae
Date:2017年11月1日23:03:26
轉(zhuǎn)載請(qǐng)注明出處

由于切換博客,2018/09/20及之前文章可能會(huì)存在圖片無(wú)法查看的問(wèn)題,請(qǐng)移步這里。


說(shuō)起Java8,可能很多人都已經(jīng)知道其最大的改進(jìn),就是引入了Lambda表達(dá)式與Stream,畢竟Java9都已近發(fā)布了,Java8發(fā)布了也已經(jīng)近三年。那么,今天我們就先來(lái)講一下Java8引入的Lambda表達(dá)式,以及由此引入的函數(shù)式編程,以及函數(shù)式接口。

什么是函數(shù)式編程

函數(shù)式編程并不是Java新提出的概念,其與指令編程相比,強(qiáng)調(diào)函數(shù)的計(jì)算比指令的計(jì)算更重要;與過(guò)程化編程相比,其中函數(shù)的計(jì)算可以隨時(shí)調(diào)用。

當(dāng)然,大家應(yīng)該都知道面向?qū)ο蟮奶匦裕ǔ橄蟆⒎庋b、繼承、多態(tài))。其實(shí)在Java8出現(xiàn)之前,我們關(guān)注的往往是某一類(lèi)對(duì)象應(yīng)該具有什么樣的屬性,當(dāng)然這也是面向?qū)ο蟮暮诵?-對(duì)數(shù)據(jù)進(jìn)行抽象。但是java8出現(xiàn)以后,這一點(diǎn)開(kāi)始出現(xiàn)變化,似乎在某種場(chǎng)景下,更加關(guān)注某一類(lèi)共有的行為(這似乎與之前的接口有些類(lèi)似),這也就是java8提出函數(shù)式編程的目的。如圖1-1所示,展示了面向?qū)ο缶幊痰矫嫦蛐袨榫幊痰淖兓?/p>

圖1-1

為什么需要Lambda表達(dá)式

首先,不得不提增加Lambda的目的,其實(shí)就是為了支持函數(shù)式編程,而為了支持Lambda表達(dá)式,才有了函數(shù)式接口。另外,為了在面對(duì)大型數(shù)據(jù)集合時(shí),為了能夠更加高效的開(kāi)發(fā),編寫(xiě)的代碼更加易于維護(hù),更加容易運(yùn)行在多核CPU上,java在語(yǔ)言層面增加了Lambda表達(dá)式。

第一個(gè)Lambda表達(dá)式

前邊廢話了這么多,其實(shí)Lambda就是Java新增的語(yǔ)法而已。當(dāng)然,Lambda(我們認(rèn)為這里包含了方法引用)確實(shí)能夠給我們的開(kāi)發(fā)帶來(lái)許多便利。
首先,在java8之前,如果需要建立一個(gè)線程,很大可能會(huì)寫(xiě)出下面的代碼:

new Thread(new Runnable()) {
    @Override
    public void run() {
        System.out.println("Hello World!");
    }
}).start();

但是Java8引入Lambda之后,也許這樣寫(xiě)會(huì)更好:

new Thread(
    () -> System.out.println("Hello world!");
);

很明顯,Lambda可以幫助我們減少模板代碼的書(shū)寫(xiě),同時(shí)減少了要維護(hù)的匿名內(nèi)部類(lèi),當(dāng)然,其作用絕不僅僅這么一點(diǎn)(關(guān)于Lambda的具體使用,讀者可以參考java8函數(shù)式編程這本書(shū),作者解析的很詳細(xì))。接下來(lái)我們先來(lái)看一下java8關(guān)于接口的的變動(dòng)。

Java8中接口的變化

其實(shí)Java9中關(guān)于接口,又有了進(jìn)一步的變動(dòng),這里我們暫且局限于Java8。在Java8中,接口可以包含靜態(tài)方法,另外還增加了一個(gè)用于修飾方法的關(guān)鍵字--default,稱(chēng)之為默認(rèn)方法(帶有方法體)。

  • 靜態(tài)方法

其實(shí)Java8中增加靜態(tài)方法,目的完全出于編寫(xiě)類(lèi)庫(kù),對(duì)某些行為進(jìn)行抽象(還記得我們之前用類(lèi)去做嗎?)。但是有一點(diǎn)不同的是:類(lèi)中的靜態(tài)方法可以繼承,并且可以從實(shí)例獲得引用(并不建議這么做);但是接口中的靜態(tài)方法不能被繼承。

  • 默認(rèn)方法

其實(shí),引入默認(rèn)方法,是不得已而為之,因?yàn)镴ava8引入了函數(shù)式接口,許多像Collection這樣的基礎(chǔ)接口中增加了方法,如果還是一個(gè)傳統(tǒng)的抽象方法的話,那么可能很多第三方類(lèi)庫(kù)就會(huì)變得完全無(wú)法使用。為了實(shí)現(xiàn)二進(jìn)制的向后兼容性,引入了帶有方法體、被default修飾的方法--默認(rèn)方法。其主要思想就是如果子類(lèi)中沒(méi)有實(shí)現(xiàn),那么采用父類(lèi)提供的默認(rèn)實(shí)現(xiàn)。其具體的繼承規(guī)則如圖1-2所示。

image

圖1-2

其中Parent接口中定義了默認(rèn)方法welcome;
Child接口對(duì)默認(rèn)方法進(jìn)行了覆蓋;
ParentImpl繼承了Parent接口的方法;
ChildImpl繼承了Child的方法;
OverridingParent覆蓋了父類(lèi)的welcome;
OverridingChild最終的welcome來(lái)自于OverridingParent。

關(guān)于繼承規(guī)則,可以簡(jiǎn)短描述為:類(lèi)勝于接口;子類(lèi)勝于父類(lèi);如果前兩者都不適用,那么子類(lèi)要么實(shí)現(xiàn)該方法,要么將該方法聲明為抽象方法。

函數(shù)式接口

關(guān)于接口的變動(dòng),Java8中新定義了一種接口類(lèi)型,函數(shù)式接口,與其他接口的區(qū)別就是:

  • 函數(shù)式接口中只能有一個(gè)抽象方法(我們?cè)谶@里不包括與Object的方法重名的方法);
  • 可以有從Object繼承過(guò)來(lái)的抽象方法,因?yàn)樗蓄?lèi)的最終父類(lèi)都是Object;
  • 接口中唯一抽象方法的命名并不重要,因?yàn)楹瘮?shù)式接口就是對(duì)某一行為進(jìn)行抽象,主要目的就是支持Lambda表達(dá)式。

Java8之前已經(jīng)存在的函數(shù)式接口有:

java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener

另外,Java8還提供了@FunctionalInterface注解來(lái)幫助我們標(biāo)識(shí)函數(shù)式接口。另外需要注意的是函數(shù)式接口的目的是對(duì)某一個(gè)行為進(jìn)行封裝,某些接口可能只是巧合符合函數(shù)式接口的定義。

如圖1-3所示,為java8的Function包的結(jié)構(gòu)(即新引入的函數(shù)式接口),圖中綠色表示主要引入的新接口,其他接口基本上都是為了支持基本類(lèi)型而添加的接口,方法的具體作用圖中有具體說(shuō)明。

[圖片上傳失敗...(image-7baf51-1537434242538)]

圖1-3

自定義函數(shù)式接口支持Lambda表達(dá)式

看下如下代碼,最終輸出應(yīng)該是兩行"Hello World!",是不是很神奇?

public class Main {
    public static void main(String[] args) {
        Action action = System.out :: println;
        action.execute("Hello World!");
        test(System.out :: println, "Hello World!");
    }

    static void test(Action action, String str) {
        action.execute(str);
    }
}
@FunctionalInterface
interface Action<T> {
    public void execute(T t);
}

小結(jié)

本文對(duì)Lambda以及函數(shù)式接口進(jìn)行了簡(jiǎn)要介紹,目的是激發(fā)大家使用Lambda的興趣,步入函數(shù)式編程的大門(mén)。

最后編輯于
?著作權(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)容

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