Java8 函數式程式設計詳解
說起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的興趣,步入函數式程式設計的大門。
Dorae轉載注明出處http://www.cnblogs.com/Dorae/ 标簽: java8