1. 概述
1.1 函數(shù)式編程簡(jiǎn)介
常見(jiàn)的編程范式:
命令式編程(Imperative Programming),邏輯式編程(Logic Programming),函數(shù)式編程(Functional Programming)。
函數(shù)式編程作為一種編程范式,在科學(xué)領(lǐng)域,是一種編寫(xiě)計(jì)算機(jī)程序數(shù)據(jù)結(jié)構(gòu)和元素的方式,它把計(jì)算過(guò)程當(dāng)做是數(shù)學(xué)函數(shù)的求值庆锦,而避免更改狀態(tài)和可變數(shù)據(jù)。
什么是函數(shù)式編程轧葛?簡(jiǎn)單的回答:一切都是數(shù)學(xué)函數(shù)搂抒。函數(shù)式編程語(yǔ)言里也可以有對(duì)象,但通常這些對(duì)象都是恒定不變的 ——
要么是函數(shù)參數(shù)尿扯,要什么是函數(shù)返回值求晶。函數(shù)式編程語(yǔ)言里沒(méi)有 for/next
循環(huán),因?yàn)檫@些邏輯意味著有狀態(tài)的改變衷笋。相替代的是,這種循環(huán)邏輯在函數(shù)式編程語(yǔ)言里是通過(guò)遞歸辟宗、把函數(shù)當(dāng)成參數(shù)傳遞的方式實(shí)現(xiàn)的。
舉個(gè)例子:
a = a +1
這段代碼在普通成員看來(lái)并沒(méi)有什么問(wèn)題泊脐,但在數(shù)學(xué)家看來(lái)確實(shí)不成立的空幻,因?yàn)樗馕吨兞恐档酶淖儭?/p>
1.2 Lambda 表達(dá)式簡(jiǎn)介
2.1 Lambda 表達(dá)式的形式
Java 中 Lambda 表達(dá)式一共有五種基本形式容客,具體如下:
?
Runnable noArguments = () -> System.out.println("Hello World");
?
ActionListener oneArgument = event -> System.out.println("button clicked");
?
Runnable multiStatement = () -> {? ? System.out.print("Hello");? ? System.out.println(" World");};
?
BinaryOperator<Long> add = (x, y) -> x + y;
?
BinaryOperator<Long> addExplicit = (Long x, Long y) -> x + y;
?中所示的 Lambda 表達(dá)式不包含參數(shù),使用空括號(hào) () 表示沒(méi)有參數(shù)。該 Lambda 表達(dá)式 實(shí)現(xiàn)了 Runnable
接口,該接口也只有一個(gè) run 方法,沒(méi)有參數(shù),且返回類(lèi)型為 void缩挑。?中所示的 Lambda
表達(dá)式包含且只包含一個(gè)參數(shù),可省略參數(shù)的括號(hào),這和例 2-2 中的 形式一樣。Lambda
表達(dá)式的主體不僅可以是一個(gè)表達(dá)式,而且也可以是一段代碼塊,使用大括號(hào)
({})將代碼塊括起來(lái),如?所示供置。該代碼塊和普通方法遵循的規(guī)則別無(wú)二致,可以用返 回或拋出異常來(lái)退出谨湘。只有一行代碼的 Lambda
表達(dá)式也可使用大括號(hào),用以明確 Lambda表達(dá)式從何處開(kāi)始悲关、到哪里結(jié)束。Lambda
表達(dá)式也可以表示包含多個(gè)參數(shù)的方法,如?所示寓辱。這時(shí)就有必要思考怎樣去閱 讀該 Lambda
表達(dá)式。這行代碼并不是將兩個(gè)數(shù)字相加,而是創(chuàng)建了一個(gè)函數(shù),用來(lái)計(jì)算 兩個(gè)數(shù)字相加的結(jié)果妇汗。變量 add 的類(lèi)型是 BinaryOperator
記住一點(diǎn)很重要籽前,Lambda 表達(dá)式都可以擴(kuò)寫(xiě)為原始的“匿名類(lèi)”形式抵赢。所以當(dāng)你覺(jué)得這個(gè) Lambda 表達(dá)式很復(fù)雜不容易理解的時(shí)候,不妨把它擴(kuò)寫(xiě)為“匿名類(lèi)”形式來(lái)看这敬。
2.2 閉包
如果你以前使用過(guò)匿名內(nèi)部類(lèi),也許遇到過(guò)這樣的問(wèn)題蕉朵。當(dāng)你需要匿名內(nèi)部類(lèi)所在方法里的變量崔涂,必須把該變量聲明為final。如下例子所示:
final String name = getUserName();
button.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent event){? ? ? ? System.out.println("hi "+ name);? ? }});
Java 8放松了這一限制始衅,可以不必再把變量聲明為final冷蚂,但其實(shí)該變量實(shí)際上仍然是final的。雖然無(wú)需將變量聲明為 final汛闸,但在 Lambda 表達(dá)式中蝙茶,也無(wú)法用作非終態(tài)變量。如果堅(jiān)持用作非終態(tài)變量(即改變變量的值)诸老,編譯器就會(huì)報(bào)錯(cuò)隆夯。
2.3 函數(shù)接口
上面例子里提到了ActionListener接口,我們看一下它的代碼:
publicinterfaceActionListenerextendsEventListener{/**
? ? * Invoked when an action occurs.
? ? */publicvoidactionPerformed(ActionEvent e);}
ActionListener只有一個(gè)抽象方法:actionPerformed别伏,被用來(lái)表示行為:接受一個(gè)參數(shù)蹄衷,返回空。記住厘肮,由于actionPerformed定義在一個(gè)接口里愧口,因此abstract關(guān)鍵字不是必需的。該接口也繼承自一個(gè)不具有任何方法的父接口:EventListener轴脐。
我們把這種接口就叫做函數(shù)接口调卑。
JDK 8 中提供了一組常用的核心函數(shù)接口:
接口參數(shù)返回類(lèi)型描述
Predicate<T>Tboolean用于判別一個(gè)對(duì)象。比如求一個(gè)人是否為男性
Consumer<T>Tvoid用于接收一個(gè)對(duì)象進(jìn)行處理但沒(méi)有返回大咱,比如接收一個(gè)人并打印他的名字
Function<T, R>TR轉(zhuǎn)換一個(gè)對(duì)象為不同類(lèi)型的對(duì)象
Supplier<T>NoneT提供一個(gè)對(duì)象
UnaryOperator<T>TT接收對(duì)象并返回同類(lèi)型的對(duì)象
BinaryOperator<T>(T, T)T接收兩個(gè)同類(lèi)型的對(duì)象恬涧,并返回一個(gè)原類(lèi)型對(duì)象
其中Cosumer與Supplier對(duì)應(yīng),一個(gè)是消費(fèi)者碴巾,一個(gè)是提供者溯捆。
Predicate用于判斷對(duì)象是否符合某個(gè)條件,經(jīng)常被用來(lái)過(guò)濾對(duì)象。
Function是將一個(gè)對(duì)象轉(zhuǎn)換為另一個(gè)對(duì)象提揍,比如說(shuō)要裝箱或者拆箱某個(gè)對(duì)象啤月。
UnaryOperator接收和返回同類(lèi)型對(duì)象,一般用于對(duì)對(duì)象修改屬性劳跃。BinaryOperator則可以理解為合并對(duì)象谎仲。
如果以前接觸過(guò)一些其他 Java 框架,比如 Google Guava刨仑,可能已經(jīng)使用過(guò)這些接口郑诺,對(duì)這些東西并不陌生。所以杉武,其實(shí) Java 8 的改進(jìn)并不是閉門(mén)造車(chē)辙诞,而是集百家之長(zhǎng)。