Java8 函數式編程詳解
Author:Dorae
Date:2017年11月1日23:03:26
轉載請注明出處
說起Java8,可能很多人都已經知道其最大的改進,就是引入了Lambda表達式與Stream,畢竟Java9都已近發布了,Java8發布了也已經近三年。那么,今天我們就先來講一下Java8引入的Lambda表達式,以及由此引入的函數式編程,以及函數式接口。
什么是函數式編程
函數式編程並不是Java新提出的概念,其與指令編程相比,強調函數的計算比指令的計算更重要;與過程化編程相比,其中函數的計算可以隨時調用。
當然,大家應該都知道面向對象的特性(抽象、封裝、繼承、多態)。其實在Java8出現之前,我們關注的往往是某一類對象應該具有什么樣的屬性,當然這也是面向對象的核心--對數據進行抽象。但是java8出現以后,這一點開始出現變化,似乎在某種場景下,更加關注某一類共有的行為(這似乎與之前的接口有些類似),這也就是java8提出函數式編程的目的。如圖1-1所示,展示了面向對象編程到面向行為編程的變化。

圖1-1
為什么需要Lambda表達式
首先,不得不提增加Lambda的目的,其實就是為了支持函數式編程,而為了支持Lambda表達式,才有了函數式接口。另外,為了在面對大型數據集合時,為了能夠更加高效的開發,編寫的代碼更加易於維護,更加容易運行在多核CPU上,java在語言層面增加了Lambda表達式。
第一個Lambda表達式
前邊廢話了這么多,其實Lambda就是Java新增的語法而已。當然,Lambda(我們認為這里包含了方法引用)確實能夠給我們的開發帶來許多便利。
首先,在java8之前,如果需要建立一個線程,很大可能會寫出下面的代碼:
new Thread(new Runnable()) {
@Override
public void run() {
System.out.println("Hello World!");
}
}).start();
但是Java8引入Lambda之后,也許這樣寫會更好:
new Thread(
() -> System.out.println("Hello world!");
);
很明顯,Lambda可以幫助我們減少模板代碼的書寫,同時減少了要維護的匿名內部類,當然,其作用絕不僅僅這么一點(關於Lambda的具體使用,讀者可以參考java8函數式編程這本書,作者解析的很詳細)。接下來我們先來看一下java8關於接口的的變動。
Java8中接口的變化
其實Java9中關於接口,又有了進一步的變動,這里我們暫且局限於Java8。在Java8中,接口可以包含靜態方法,另外還增加了一個用於修飾方法的關鍵字--default,稱之為默認方法(帶有方法體)。
- 靜態方法
其實Java8中增加靜態方法,目的完全出於編寫類庫,對某些行為進行抽象(還記得我們之前用類去做嗎?)。但是有一點不同的是:類中的靜態方法可以繼承,並且可以從實例獲得引用(並不建議這么做);但是接口中的靜態方法不能被繼承。
- 默認方法
其實,引入默認方法,是不得已而為之,因為Java8引入了函數式接口,許多像Collection這樣的基礎接口中增加了方法,如果還是一個傳統的抽象方法的話,那么可能很多第三方類庫就會變得完全無法使用。為了實現二進制的向后兼容性,引入了帶有方法體、被default修飾的方法--默認方法。其主要思想就是如果子類中沒有實現,那么采用父類提供的默認實現。其具體的繼承規則如圖1-2所示。

圖1-2
其中Parent接口中定義了默認方法welcome;
Child接口對默認方法進行了覆蓋;
ParentImpl繼承了Parent接口的方法;
ChildImpl繼承了Child的方法;
OverridingParent覆蓋了父類的welcome;
OverridingChild最終的welcome來自於OverridingParent。
關於繼承規則,可以簡短描述為:類勝於接口;子類勝於父類;如果前兩者都不適用,那么子類要么實現該方法,要么將該方法聲明為抽象方法。
函數式接口
關於接口的變動,Java8中新定義了一種接口類型,函數式接口,與其他接口的區別就是:
- 函數式接口中只能有一個抽象方法(我們在這里不包括與Object的方法重名的方法);
- 可以有從Object繼承過來的抽象方法,因為所有類的最終父類都是Object;
- 接口中唯一抽象方法的命名並不重要,因為函數式接口就是對某一行為進行抽象,主要目的就是支持Lambda表達式。
Java8之前已經存在的函數式接口有:
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注解來幫助我們標識函數式接口。另外需要注意的是函數式接口的目的是對某一個行為進行封裝,某些接口可能只是巧合符合函數式接口的定義。
如圖1-3所示,為java8的Function包的結構(即新引入的函數式接口),圖中綠色表示主要引入的新接口,其他接口基本上都是為了支持基本類型而添加的接口,方法的具體作用圖中有具體說明。

圖1-3
自定義函數式接口支持Lambda表達式
看下如下代碼,最終輸出應該是兩行"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);
}
小結
本文對Lambda以及函數式接口進行了簡要介紹,目的是激發大家使用Lambda的興趣,步入函數式編程的大門。
