天天看點

Java8 函數式程式設計詳解Java8 函數式程式設計詳解什麼是函數式程式設計為什麼需要Lambda表達式第一個Lambda表達式Java8中接口的變化函數式接口自定義函數式接口支援Lambda表達式小結

Java8 函數式程式設計詳解

說起Java8,可能很多人都已經知道其最大的改進,就是引入了Lambda表達式與Stream,畢竟Java9都已近釋出了,Java8釋出了也已經近三年。那麼,今天我們就先來講一下Java8引入的Lambda表達式,以及由此引入的函數式程式設計,以及函數式接口。

什麼是函數式程式設計

函數式程式設計并不是Java新提出的概念,其與指令程式設計相比,強調函數的計算比指令的計算更重要;與過程化程式設計相比,其中函數的計算可以随時調用。

當然,大家應該都知道面向對象的特性(抽象、封裝、繼承、多态)。其實在Java8出現之前,我們關注的往往是某一類對象應該具有什麼樣的屬性,當然這也是面向對象的核心--對資料進行抽象。但是java8出現以後,這一點開始出現變化,似乎在某種場景下,更加關注某一類共有的行為(這似乎與之前的接口有些類似),這也就是java8提出函數式程式設計的目的。如圖1-1所示,展示了面向對象程式設計到面向行為程式設計的變化。

Java8 函數式程式設計詳解Java8 函數式程式設計詳解什麼是函數式程式設計為什麼需要Lambda表達式第一個Lambda表達式Java8中接口的變化函數式接口自定義函數式接口支援Lambda表達式小結

圖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所示。

Java8 函數式程式設計詳解Java8 函數式程式設計詳解什麼是函數式程式設計為什麼需要Lambda表達式第一個Lambda表達式Java8中接口的變化函數式接口自定義函數式接口支援Lambda表達式小結

圖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包的結構(即新引入的函數式接口),圖中綠色表示主要引入的新接口,其他接口基本上都是為了支援基本類型而添加的接口,方法的具體作用圖中有具體說明。

Java8 函數式程式設計詳解Java8 函數式程式設計詳解什麼是函數式程式設計為什麼需要Lambda表達式第一個Lambda表達式Java8中接口的變化函數式接口自定義函數式接口支援Lambda表達式小結

圖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

繼續閱讀