1. 程式人生 > >Java8學習筆記之Lambda表示式

Java8學習筆記之Lambda表示式

Lambda.jpg
使用Lambda表示式,我們可以很簡潔地傳遞程式碼(通常是匿名函式)。

結構

Lambda表示式主要分為三部分:引數列表,箭頭,Lambda 主體

語法

  • (parameters) -> expression
  • (parameters) -> { statements; }

如果表示式只有一行,用第一種,多行用第二種。

Java8中,標註了@FunctionalInterface,表明這個介面將是一個函式式介面,它裡面只能有一個抽象方法。

常用的函式式介面

JDK已經為我們提供了很多常用的函式式介面:
* Predicate:java.util.function.Predicate<T>

介面定義了一個名叫test的抽象方法,它接受泛型T物件,並返回一個boolean。在需要表示一個涉及型別T的布林表示式時可以使用。
* Consumer:java.util.function.Consumer<T>定義了一個名叫accept的抽象方法,它接受泛型T的物件,沒有返回(void)。如果需要訪問型別T的物件,並對其執行某些操作,就可以使用這個介面。
* Supplier:java.util.function.Supplier<T>不接受物件,返回一個泛型物件T。在需要new一個物件例項時可以使用。
* Function:java.util.function.Function<T, R>
介面定義了一個叫作apply的方法,它接受一個泛型T的物件,並返回一個泛型R的物件。如果需要定義一個Lambda,將輸入物件的資訊對映到輸出,就可以使用這個介面。

原始型別特化

我們知道,泛型只能繫結到引用型別的物件。因此,在使用泛型繫結基本型別的時候,Java會為我們自動裝箱和拆箱,但這是會消耗效能的。
如果輸入和輸出都是基本型別時,Java8為我們提供了新的函式式介面,以避免自動裝箱拆箱。

簡單列舉一部分:
* Predicate:IntPredicate, LongPredicate, DoublePredicate
* Consumer:IntConsumer,LongConsumer

, DoubleConsumer
* Supplier:BooleanSupplier, IntSupplier, LongSupplier, DoubleSupplier
* Function:IntFunction<R>,LongToDoubleFunction,ToLongFunction<T>

從命名可以輕易看出從什麼型別轉成什麼型別,可以在java.util.function包下檢視所有介面。

使用區域性變數

在使用lambda時,主體程式碼塊內允許使用的外部變數。但是,不允許改變外部變數。這些變數應該宣告為final或者事實上是final的(即之後程式碼中不會改變)

方法引用

方法引用主要有三類:

  • 指向靜態方法的方法引用
    • Lambda: (args) -> ClassName.staticMethod(args)
    • 方法引用:ClassName :: staticMethod
  • 指向任意型別例項方法的方法引用
    • Lambda: (arg0, rest) -> arg0.instanceMethod(rest)
    • 方法引用:ClassName :: instanceMethod(arg0 是 ClassName 型別的)
  • 指向現有物件的例項方法的方法引用
    • Lambda: (args) -> expr.instanceMethod(args)
    • 方法引用:expr :: intanceMethod

除此之外,還有建構函式引用:ClassName :: new
比如用Map來將建構函式對映到字串值:

    static Map<String, Function<Integer, Fruit>> map = new HashMap<>();
    static {
        map.put("apple", Apple::new);
        map.put("orange", Orange::new);
        // etc...
    }

    public static Fruit giveMeFruit(String fruit, Integer weight) {
        return map.get(fruit.toLowerCase()).apply(weight);
    }

複合 Lambda 表示式

Comparator、Predicate和Function等函式式介面都有幾個可以用來結Lambda表示式的預設方法。

比較器複合

  1. 普通排序comparing()
Comparator<Apple> c = Comparator.comparing(Apple::getWeight);
  1. 逆序reversed()
inventory.sort(comparing(Apple::getWeight).reversed()); 
  1. 比較器鏈thenComparing()
inventory.sort(comparing(Apple::getWeight).reversed()
    .thenComparing(Apple::getCountry));

謂詞複合

3個方法增強已有的Predicate介面:
* and:與
* or:或
* negate:非

請注意,and和or方法是按照在表示式鏈中的位置,從左向右確定優先順序的。因此,a.or(b).and(c)可以看作(a || b) && c。

函式複合

Function介面有andThencompose兩個預設方法,它們都會返回Function的一個例項。

舉個例子:
有2個函式,一個加1,一個乘2

Function<Integer, Integer> f = x -> x + 1; // f(x)=x+1
Function<Integer, Integer> g = x -> x * 2; // g(x)=2x
  1. andThen()
Function<Integer, Integer> h = f.andThen(g); // g(f(x))
int result = h.apply(1); // 4
  1. compose()
Function<Integer, Integer> h = f.compose(g); // f(g(x))
int result = h.apply(1); // 3