寫在有道筆記中,鏈接地址。歡迎各位看官提出意見交流討論
🔗http://note.youdao.com/noteshare?id=147109f1bf7f3ae97c43d77891e6ebc8
Lambda表達式和函數式接口
一.Lambda表達式
1.定義
百度百科上如下定義: “Lambda 表達式”(lambda expression)是一個匿名函數,Lambda表達式基於數學中的λ演算得名,直接對應於其中的lambda抽象(lambda abstraction),是一個匿名函數,即沒有函數名的函數。Lambda表達式可以表示閉包(注意和數學傳統意義上的不同)。
2.java中的Lambda
java是一個面向對象的語言,而Lambda表達式卻是一個匿名函數,因此java把Lambda表達式抽象成一個匿名內部類(jdk中沒有抽象出來,但是它是一個匿名內部類的實現,在下面的截圖中,很明顯可以看到是一個內部類的地址----【LambdaTestDemo$lambda@679】)。
為了證實如上所說,我就隨手寫了一個lambda表達式,來調試一下。
package com.gcl.jdk8.lambdatest; public class LambdaTestDemo1 { public static void main(String[] args) { //寫一個函數接口指向Lambda表達式,主要為了看到lambda表達式在傳遞時候的狀態 MyFunctionInterface myFunctionInterface = () -> System.out.println("a"); myFunctionInterface.doSome(); } } @FunctionalInterface interface MyFunctionInterface{ void doSome(); }
在debug狀態下,我們可以通過watch清楚的看到lambda表達式是一個內部類。清楚了這一點之后,我想對我們理解lambda表達式有着重要的幫助 
3.基本結構
我個人將Lambda表達式分為3塊,分別是參數模塊,->,代碼塊。類似下面的形式。
//真正的表達應該是這樣子的 (param1,param2,...,paramn) -> {code body }
那我就分三塊來說明一下
a. 參數模塊
1.參數類型可以省略
參數模塊顧名思義,就是代表着傳入Lambda表達式的代碼的參數。一般說來我們需要定義傳入參數的類型,但是在大部份情況下,jdk會通過上下文去推斷這個參數是什么類型,那么就不需要定義了。下面這塊代碼中,申明 t1的類型的語句可以省略。我在網上查閱了一些人的博客,他們有的把申明類型的語句留着,我想這樣的做法第一是跟之前的java的寫法保持一致性,第二就是開發的時候雖然jdk可以自己推斷,但是開發人員如果還要去推斷一下就太麻煩了,大概就是這么些個理由。
2.()可以省略
在參數只有1個的時候,()可以省略。同時也推薦省略。但是如果要申明類型的話,不能省略。
package com.gcl.jdk8.lambdatest; public class LambdaTestDemo1 { public static void main(String[] args) { //寫一個函數接口指向Lambda表達式,主要為了看到lambda表達式在傳遞時候的狀態 Person p = new Person("張三",12); //MyFunctionInterface<String> myFunctionInterface = (String t1) -> p.setAge(1); MyFunctionInterface<String> myFunctionInterface = t1 -> p.setAge(1); myFunctionInterface.doSome("a"); } } @FunctionalInterface interface MyFunctionInterface<T>{ void doSome(T t); }
b.->
這個沒什么好說的,寫一般的Lambda表達式必須寫的,這是用來區分參數模塊和代碼模塊的一個標志,但是在特殊Lambda表達式中也是可以寫的,在后面我會具體寫一寫這種特殊的Lambda表達式(方法引用)
c.{} 代碼模塊
代碼模塊的具體作用就是處理邏輯的具體實現。 需要注意的是,在具體實現的時候,每個分之都必須滿足其返回值,這也好比是函數,規定了返回值類型,如果某一個分之返回與之不合的返回值類型,那就肯定錯了。
//例如在函數接口中規定了返回值是范型T,如果返回了其他類型的就出錯了 @FunctionalInterface interface MyFunctionInterface<T>{ T doSome(T t); } public static void main(String[] args) { //寫一個函數接口指向Lambda表達式,主要為了看到lambda表達式在傳遞時候的狀態 Person p = new Person("張三", 12); MyFunctionInterface<String> myFunctionInterface = t1 -> { if (t1.equals("true")) { return "true"; } else { //return Boolean.FALSE; 這樣的返回值就錯了 return "false"; } }; myFunctionInterface.doSome("a"); }
MyFunctionInterface<String> myFunctionInterface2 = t1 -> t1;
注意:
1.單條語句時候,且是表達式語句(expression)的時候{}可以省略,例如
MyFunctionInterface<String> myFunctionInterface2 = t1 -> t1;
2.單條語句時候,且是return語句(statement)的時候{}可以不可以省略,**;**不可以省略,例如
MyFunctionInterface<String> myFunctionInterface2 = t1 -> {return t1;};
二. 函數式接口
1.定義: 只有一個抽象方法的接口。
定義十分簡單,但是卻有一些比較關鍵的字眼,我抽取其中三個詞:一個,抽象,接口。
一個:只有一個抽象方法,多了不行,少了也不行。同時,這個抽象方法一定不能跟Object類中的方法同名。
/** * 比如說,MyFunctionInterface這個接口,抽象方法是equal方法,其實他是覆蓋了(重寫)Object類中的equals方法。加上@FunctionalInterface注解之后,讓編譯器取做一個顯示檢查是否這個接口只含有一個抽象方法。這樣的接口是一個錯誤的接口示例。 */ @FunctionalInterface interface MyFunctionInterface<T> { //根據阿里巴巴java開發手冊上的規定,如果是重寫的話,強制加上@Override注解 @Override public boolean equals(T t); }
2.配合Lambda表達式的具體使用
一個自定義函數接口,與之對應的Lambda表達式的參數就是這個默認方法的參數,返回值就是這個默認方法的返回值。
@FunctionalInterface interface MyFunctionInterface<String> { String doSome(String t); }
- 五個常用的函數接口
java提供的函數接口在java.util.function包中,其中比較重要的是以下5個接口。我認為jdk8給我們提供的這些接口是提供了一些模式(幫助我們抽象出來了一些行為),在使用的時候根據官方抽象的接口進行實現也更加方便。
a.Consumer (消費模式)
功能:傳遞一個參數,無返回
類定義如下:
package java.util.function; import java.util.Objects; /** * Represents an operation that accepts a single input argument and returns no * result. Unlike most other functional interfaces, {@code Consumer} is expected * to operate via side-effects. * * * <p>This is a <a href="package-summary.html">functional interface</a> * whose functional method is {@link #accept(Object)}. * * @param <T> the type of the input to the operation * * @since 1.8 */ @FunctionalInterface public interface Consumer<T> { /** * Performs this operation on the given argument. * * Consumer中的默認方法,表示接受一個參數且沒有返回值 * * @param t the input argument */ void accept(T t); /** * Returns a composed {@code Consumer} that performs, in sequence, this * operation followed by the {@code after} operation. If performing either * operation throws an exception, it is relayed to the caller of the * composed operation. If performing this operation throws an exception, * the {@code after} operation will not be performed. * * 默認方法,提供鏈式調用方式執行。執行流程:先執行本身的accept在執行傳入參數after.accept方法。 * 該方法會拋出NullPointerException異常。 * 如果在執行調用鏈時出現異常,會將異常傳遞給調用鏈功能的調用者,且發生異常后的after將不會在調用。 * * @param after the operation to perform after this operation * @return a composed {@code Consumer} that performs in sequence this * operation followed by the {@code after} operation * @throws NullPointerException if {@code after} is null */ default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
//隨手寫了一個代碼 package com.gcl.jdk8.lambdatest; import java.util.function.Consumer; public class ConsumerTest { public static void main(String[] args) { //普通的Lambda,表示接受一個參數,void 返回值 Consumer<String> consumer = s -> System.out.println(s); //再簡化一下,特殊的Lambda表達式,方法引用 Consumer<String> consumer2 = System.out::println; //調用一下,情況如何? consumer.accept("a1"); consumer2.accept("a2"); } }
執行結果如下所示,其實就是一個打印字符串的函數,沒什么好說的,我們繼續下一個接口。

b.Supplier (生產模式)
功能:不傳遞參數,返回一個值。我們來看一下源碼
package java.util.function;
/**
* Represents a supplier of results. * 其實就是不接受任何參數,返回這個接口申明的范型,通過get()來進行調用 * <p>There is no requirement that a new or distinct result be returned each * time the supplier is invoked. * * <p>This is a <a href="package-summary.html">functional interface</a> * whose functional method is {@link #get()}. * * @param <T> the type of results supplied by this supplier * * @since 1.8 */ @FunctionalInterface public interface Supplier<T> { /** * Gets a result. * * @return a result */ T get(); }
生產者(可以理解為,不接受任何參數,返回一個值,究竟返回什么值由調用者決定----Lambda表達式的制造者)
package com.gcl.jdk8.lambdatest; import java.util.function.Supplier; public class SupplierTest { public static void main(String[] args) { //普通的lambda Supplier<String> supplier1 = () -> "nice"; //特殊的lambda Supplier<String> supplier2 = String::new; String s1 = supplier1.get(); String s2 = supplier2.get(); //打印s1 System.out.println(s1); //打印s2 System.out.println(s2); } }
c.Function (功能模式)
功能:傳遞1個參數,返回1個參數。現在依舊來看一下源碼
package java.util.function; import java.util.Objects; /** * Represents a function that accepts one argument and produces a result. * * 接受一個參數返回一個結果,T是輸入范型,R是輸出范型 * * <p>This is a <a href="package-summary.html">functional interface</a> * whose functional method is {@link #apply(Object)}. * * @param <T> the type of the input to the function * @param <R> the type of the result of the function * * @since 1.8 */ @FunctionalInterface public interface Function<T, R> { /** * Applies this function to the given argument. * * @param t the function argument * @return the function result */ R apply(T t); /** * Returns a composed function that first applies the {@code before} * function to its input, and then applies this function to the result. * If evaluation of either function throws an exception, it is relayed to * the caller of the composed function. * * @param <V> the type of input to the {@code before} function, and to the * composed function * @param before the function to apply before this function is applied * @return a composed function that first applies the {@code before} * function and then applies this function * @throws NullPointerException if before is null * * @see #andThen(Function) */ default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } /** * Returns a composed function that first applies this function to * its input, and then applies the {@code after} function to the result. * If evaluation of either function throws an exception, it is relayed to * the caller of the composed function. * * @param <V> the type of output of the {@code after} function, and of the * composed function * @param after the function to apply after this function is applied * @return a composed function that first applies this function and then * applies the {@code after} function * @throws NullPointerException if after is null * * @see #compose(Function) */ default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } /** * Returns a function that always returns its input argument. * * @param <T> the type of the input and output objects to the function * @return a function that always returns its input argument */ static <T> Function<T, T> identity() { return t -> t; } }
同樣是舉個例子
package com.gcl.jdk8.lambdatest;
import java.util.function.Function; public class FunctionTest { public static void main(String[] args) { //接受一個值,返回一個Boolean值 Function<Integer,Boolean> function = (i) -> { if(i>1){ return true; } else { return false; } }; Boolean result = function.apply(3); System.out.println(result); } }
在Function<T,R>這個函數式接口中還有着其他方法【compose】,【andThen】,【identity】,這三個方法其實也是其他函數式接口中可能存在的方法(在BiFunction這樣的接口中,compose方法是不存在的),對於這樣的方法來說在每個函數接口中都是有着自己的實現,只不過我們在讀取源碼之后發現他在每個函數式接口中的實現都大致相似,因此在Function這個接口中進行一個分析,其他的接口中就不分析了。
1.compose方法,在javadoc中如下描述。 Returns a composed function that first applies the {@code before},其實很簡單就是一個å前置執行,需要注意的是它傳入的是一個Function接口,返回的也是一個Function接口。
/**
* Returns a composed function that first applies the {@code before} * function to its input, and then applies this function to the result. * If evaluation of either function throws an exception, it is relayed to * the caller of the composed function. * * @param <V> the type of input to the {@code before} function, and to the * composed function * @param before the function to apply before this function is applied * @return a composed function that first applies the {@code before} * function and then applies this function * @throws NullPointerException if before is null * * @see #andThen(Function) */ default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); }
同樣是舉個例子
package com.gcl.jdk8.lambdatest;
import java.util.function.Function; public class FunctionTest { public static void main(String[] args) { //接受一個值,返回一個Boolean值 Function<Integer, Integer> function1 = i -> { if (i > 4) { return 1; } else { return 2; } }; Function<Integer, Integer> function2 = i -> i * i; //在function2執行之前,先執行function1,猜測一下 1小於4返回2,2*2 = 4 Integer integer = function2.compose(function1).apply(1); System.out.println(integer); //在function1執行之前,先執行function2,猜測一下 1*1 = 1 ,比4小返回2 Integer integer2 = function1.compose(function2).apply(1); System.out.println(integer2); } }
2.andThen也非常好理解了,就是一個后置執行,通過一個例子看一下。
package com.gcl.jdk8.lambdatest;
import java.util.function.Function; public class FunctionTest { public static void main(String[] args) { //接受一個值,返回一個Boolean值 Function<Integer, Integer> function1 = i -> { if (i > 4) { return 1; } else { return 2; } }; Function<Integer, Integer> function2 = i -> i * i; //先執行f1,在執行f2,2*2 System.out.println(function1.andThen(function2).apply(1)); //先執行f2,在執行f1,2 -> 1 System.out.println(function2.andThen(function1).apply(1)); } }
3.identity這個靜態方法根據javadoc中的描述,返回的是一個function,返回自己本身。
/**
* Returns a function that always returns its input argument. * * @param <T> the type of the input and output objects to the function * @return a function that always returns its input argument */ static <T> Function<T, T> identity() { return t -> t; }
package com.gcl.jdk8.lambdatest;
import java.util.function.Function; import java.util.function.Supplier; public class FunctionTest { public static void main(String[] args) { //Supplier去返回一個Function的identity方法 Supplier<Function> supplier = Function::identity; //輸出自己 -- t -> t System.out.println(supplier.get().apply(1)); } }
d.Predicate (判斷模式--謂詞)
功能: 傳遞一個參數,返回一個Boolean值。先看javadoc
package java.util.function; import java.util.Objects; /** * Represents a predicate (boolean-valued function) of one argument. * * <p>This is a <a href="package-summary.html">functional interface</a> * whose functional method is {@link #test(Object)}. * * @param <T> the type of the input to the predicate * * @since 1.8 */ @FunctionalInterface public interface Predicate<T> { /** * Evaluates this predicate on the given argument. * * @param t the input argument * @return {@code true} if the input argument matches the predicate, * otherwise {@code false} */ boolean test(T t); /** * Returns a composed predicate that represents a short-circuiting logical * AND of this predicate and another. When evaluating the composed * predicate, if this predicate is {@code false}, then the {@code other} * predicate is not evaluated. * * <p>Any exceptions thrown during evaluation of either predicate are relayed * to the caller; if evaluation of this predicate throws an exception, the * {@code other} predicate will not be evaluated. * * @param other a predicate that will be logically-ANDed with this * predicate * @return a composed predicate that represents the short-circuiting logical * AND of this predicate and the {@code other} predicate * @throws NullPointerException if other is null */ default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } /** * Returns a predicate that represents the logical negation of this * predicate. * * @return a predicate that represents the logical negation of this * predicate */ default Predicate<T> negate() { return (t) -> !test(t); } /** * Returns a composed predicate that represents a short-circuiting logical * OR of this predicate and another. When evaluating the composed * predicate, if this predicate is {@code true}, then the {@code other} * predicate is not evaluated. * * <p>Any exceptions thrown during evaluation of either predicate are relayed * to the caller; if evaluation of this predicate throws an exception, the * {@code other} predicate will not be evaluated. * * @param other a predicate that will be logically-ORed with this * predicate * @return a composed predicate that represents the short-circuiting logical * OR of this predicate and the {@code other} predicate * @throws NullPointerException if other is null */ default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); } /** * Returns a predicate that tests if two arguments are equal according * to {@link Objects#equals(Object, Object)}. * * @param <T> the type of arguments to the predicate * @param targetRef the object reference with which to compare for equality, * which may be {@code null} * @return a predicate that tests if two arguments are equal according * to {@link Objects#equals(Object, Object)} */ static <T> Predicate<T> isEqual(Object targetRef) { return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); } }
我在例子中寫了一個predicate和一個function,可以看出,pedicate就是一個具體化的function,它規定了它的出參為boolean,為了證實我的想法,我翻閱了java核心技術卷I,第240頁,在Predicate的描述中寫到,布爾值的函數。當然他也有一個或操作和一些與操作,感興趣的小伙伴可以去試一下。
package com.gcl.jdk8.lambdatest;
import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; public class FunctionTest { public static void main(String[] args) { Predicate<Integer> predicate = integer -> { if (integer > 3){ return true; } else { return false; } }; Function<Integer,Boolean> function = integer -> { if (integer > 3){ return true; } else { return false; } }; } }
e.BiXXXX (BiFunction為例)
功能: 傳遞兩個個參數,得到一個返回值。
import java.util.Objects;
/**
* Represents a function that accepts two arguments and produces a result. * This is the two-arity specialization of {@link Function}. * * <p>This is a <a href="package-summary.html">functional interface</a> * whose functional method is {@link #apply(Object, Object)}. * * @param <T> the type of the first argument to the function * @param <U> the type of the second argument to the function * @param <R> the type of the result of the function * * @see Function * @since 1.8 */ @FunctionalInterface public interface BiFunction<T, U, R> { /** * Applies this function to the given arguments. * * @param t the first function argument * @param u the second function argument * @return the function result */ R apply(T t, U u); /** * Returns a composed function that first applies this function to * its input, and then applies the {@code after} function to the result. * If evaluation of either function throws an exception, it is relayed to * the caller of the composed function. * * @param <V> the type of output of the {@code after} function, and of the * composed function * @param after the function to apply after this function is applied * @return a composed function that first applies this function and then * applies the {@code after} function * @throws NullPointerException if after is null */ default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t, U u) -> after.apply(apply(t, u)); }
//返回3 public class FunctionTest { public static void main(String[] args) { BiFunction<Integer,Integer,Integer> biFunction= (i1,i2) -> i1+i2; System.out.println(biFunction.apply(1,2)); } }
需要注意的地方是BiFunction沒有compose方法,因為BiFunction需要的是兩個參數,如果有compsose方法的話,那沒有辦法在一個方法返回兩個返回值。
總結
Lambda表達式和函數式接口分享暫時結束,文章中可能有我理解錯誤的地方,歡迎大家指正,如果需要分享的話請注明出處,下一次分享就是java8中的流操作。
