五分鐘學習 Java 8 行爲參數化

一、概覽

Java8的改進比歷史上任何一次改變都比較深遠。Java不斷改進也是編程語言生態變化的使然——諸如大數據須要在多核上面運行,而Java此前是不支持這種操做的。express

在Java8以前,若是想要利用多個計算機的內核,你要使用線程,而且要處理複雜的同步邏輯。可是在Java8中,你能夠很容易地使用流讓本身的代碼在多個內核上面執行。編程

此外,它還借鑑了其餘語言和開源庫的內容,好比Scala、Guava等。咱們總結一下Java8的主要幾個特徵或者改進:app

  1. 函數式編程和Lambda表達式;
  2. 流(Stream)編程;
  3. 時間API的改進;
  4. 默認方法

二、行爲參數化

「行爲」就是指方法,「行爲參數化」就是指將方法做爲參數傳入,說白了就是指策略模式。而Java8只是使用了Lambda表達式簡化了匿名類的代碼,使匿名類看起來更加簡潔。在這塊內容上,你所須要掌握的東西並很少。編程語言

2.1 Lambda表達式基本語法

(parameters) -> expression     // 表達式
(parameters) -> {statements;}  // 語句,語句尾帶分號且要用花括號括起來
複製代碼

上面是Lambda的基本語法,第一行中是Lambda中使用表達式的狀況,第二行中式Lambda中使用語句的狀況。函數式編程

下面是一些使用Lambda表達式的示例:函數

public static void main(String...args) {
    // 建立對象
    ICreateObject createObject = Employee::new;
    IExpression expression = employees -> employees.get(0);
    // 能夠進一步簡化爲 IExpression expression2 = List::isEmpty;
    IExpression expression2 = employees -> employees.isEmpty(); 
    IConsumeObject consumeObject = employee -> System.out.println(employee.name);
    IAdd add = (a, b) -> a + b;
    IAdd add1 = Java8LambdaExample::cal;
    // 會報出不是Function接口異常
    //        Object object = Employee::new;
}
複製代碼

從上面的示例代碼,咱們能夠總結出一些結論:大數據

  1. 所謂的函數接口就是指只包含一個非默認方法的接口,能夠用@FunctionalInterface註解標明指定的接口是函數接口;
  2. 若是Lambda中的->後面的是語句,而且當該語句只有一行的時候,咱們能夠將花括號去掉;
  3. 想要將Lambda表達式賦值給一個對象的時候,若是這個對象不是函數接口,那麼IDEA會給提示;
  4. 還要注意函數式接口是不容許拋出受檢異常的。

下面咱們總結一些常見的方法引用的示例:spa

上面代碼中的Employee::new就是所謂的方法引用,下面是常見的方法引用的例子:線程

編號 Lambda 等效的方法引用
1 (Employee e)->e.getName() Employee::getName
2 (String s) -> System.out.println(s) System.out::println
3 (str, i) -> str.substring(i) String::substring

因此,咱們總結下來的三種方法引用的情形:code

編號 Lambda 等效的方法引用
1 (參數) -> 類名.靜態方法(參數) 類名::靜態方法
2 (參數1, 其餘參數) -> 參數1.實例方法(其餘參數) 類名::實例方法
3 (參數) -> 表達式.實例方法(參數) 表達式::實例方法

2.2 Java API 中的函數式接口

Java8的API中爲咱們提供了幾個函數式接口,這些接口有必要了解一下。由於自從Java8開始接口能夠定義默認方法了,因此這些接口裏面又提供了一些有意思的默認方法。這可能對咱們編程比較有幫助。

public interface Predicate<T> {
    boolean test(T t);
}

public interface Consumer<T> {
    void accept(T t);
}

public interface Function<T, R> {
    R apply(T t);
}
複製代碼

上面就是這三個接口的定義。它們的應用場景的不一樣就體現再返回的參數上面:

  1. 第一個用來判斷的,大體用來實現過濾的效果;
  2. 第二是沒有返回類型,只能用來對傳入的參數進行處理;
  3. 第三個是用來映射的,也就是說,當你想要實現的行爲的參數和返回是不一樣的類型的時候能夠用它(固然,若是是相同類型的話也是能夠的)。

由於對於數值類型,Java須要作額外的裝箱和拆箱的操做,這是須要成本的。因此,對於上面的三個接口(其餘的接口也是),Java8中提供了不須要裝箱的版本,也就是從泛型變成了數值類型而已。以IntPredicate爲例:

public interface IntPredicate {
    boolean test(int value);
}
複製代碼

2.3 複合Lambda表達式

Java8中提供的一些接口仍是能夠複合操做的。使用複合操做能夠實現更復雜的邏輯。這些複合操做是以默認方法的形式定義的,每一個函數式接口略有不一樣。因此,咱們這裏只列舉出部分用於複合的方法。在實際的開發過程當中,你能夠直接進入到指定的函數式接口中查看這些方法的定義。

2.3.1 比較器Comparator

假設有一個數據列表employees,其中的對象是Employee,它有getName()和getAge()兩個方法。

employees.sort(Comparator.comparing(Employee::getName));
employees.sort(Comparator.comparing(Employee::getName).reversed().thenComparing(Employee::getAge));
複製代碼

上面的兩行代碼中,第一行實現對employees按照getName()的結果進行排序。第二行代碼對employees,先按照getName()的結果進行排序,而後將返回的結果逆序,再按照getAge()的結果進行排序。

2.3.2 謂詞複合 negate()、or()和and()

Predicate<Employee> employeePredicate = (employee -> employee.getAge() > 13)
employeePredicate.negate()
employeePredicate.and(employee -> employee.getAge() <= 15).or(employee -> "LiHua".equals(employee.getName()))
複製代碼

這裏首先定義了employeePredicate,它能夠用來過濾「年齡大於13的僱員」。對其調用了negate()方法將返回一個Predicate,能夠用來過濾「年紀小於等於13的僱員」。最後的複合操做則表示「年齡大於13而且小於15的僱員或者名字爲LiHua的僱員」。

注意,這裏的and和or操做的順序是從左向右的,也就是a.or(b).and(c)將被看做(a || b) and c

2.3.3 函數Function複合

Function有andThen和compose兩個默認方法,它們都會返回一個Function實例。

Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h1 = f.andThen(g); // h1(x) = g(f(x)) = (x + 1) * 2
Function<Integer, Integer> h2 = f.compose(g); // h2(x) = f(g(x)) = (x * 2) + 1
System.out.println(h1.apply(1));
System.out.println(h2.apply(1));
複製代碼

上面是Function的複合操做的示例,其實它的效果就至關於數學中的複合函數。不過,應當注意一下兩個方法的實際的複合效果是不一樣的。

總結

以上就是Java8改進的第一部分,總結一下:行爲參數化其實就是策略模式,使用Lambda能夠簡化函數接口的形式;Java API中提供了一些有用的函數式接口,這些接口又可使用複合方法實現更增強大的功能。

相關文章
相關標籤/搜索