【java8新特性】——lambda表达式与函数式接口详解(一)
一、簡介
java8于2014年發布,相比于java7,java8新增了非常多的特性,如lambda表達式、函數式接口、方法引用、默認方法、新工具(編譯工具)、Stream API、Date Time API、Optional等 。 當前很多公司的老產品依然使用的java7,甚至開發人員開發新產品時依然沒有選擇升級, 寫關于java8系列文章的目的在于梳理和分享java8新增的主要特性,開發時也可以用作參考。
lambda表達式是java8新增的主要特性之一,lambda表達式又稱閉包或匿名函數,主要優點在于簡化代碼、增強代碼可讀性、并行操作集合等。至于是否使用,有的同學覺得不適應,有的同學欲罷不能,見仁見智~
技多不壓身,本文將采用由淺入深的方式,講解java8 lambda表達式的語法及使用,并附帶代碼進行演示。
二、lambda語法
lambda的基本語法:
(parameters) -> expressionor(parameters) ->{ statements; }lambda表達式的特性:
特性示例:
//1、無參數,返回值1 () -> 1 //2、無參數,無返回值 () -> System.out.print("Java8 lambda."); //3、1個參數,參數類型為數字,返回值為其值的5倍 x -> 5 * x //4、2個參數,參數類型均為數字,返回值為其差值 (x, y) -> x - y //5、2個參數,指定參數類型均為int型,返回值為其差值 (int x, int y) -> x - y //6、1個參數,指定參數類型為String ,無返回值 (String str) -> System.out.print(str)三、java8 lambda使用示例
前面我們講到lambda表達式的語法和特性,那么在java8中如何使用lambda表達式呢?我們先以用幾個示例來展現lambda表達式在java8中的使用。
3.1 java Runnable接口的lambda實現
用lambdah代替匿名類是java8中lambda的常用形式,本文以開發同學經常使用的Runnable接口匿名類為示例,演示如何用lambda表達式來代替匿名類:
可以看到,java8中利用lambda表達式大大簡化了代碼編寫。
此處簡要提下,用lambda表達式代替匿名類的關鍵在于,匿名類實現的接口使用了java.lang.FunctionalInterface注解,且只有一個待實現的抽象接口方法,如Runnable接口:
本文后面會詳細講解FunctionalInterface注解。
3.2 java List迭代的lambda實現
開發同學經常會使用到集合類,并對集合類對象進行迭代,以實現業務邏輯。
java8中,集合類的頂層接口java.lang.Iterable定義了一個forEach方法:
forEach方法可以迭代集合的所有對象,其參數為Consumer對象,Consumer類位于java.util.function包下,我們看下其定義:
@FunctionalInterface public interface Consumer<T> {void accept(T t);default Consumer<T> andThen(Consumer<? super T> after) {Objects.requireNonNull(after);return (T t) -> { accept(t); after.accept(t); };} }到此已經很容易聯想到,我們可以采用lambda表達式來實現java8集合的迭代邏輯,下面我們進行示例:
上述邏輯還可以用java8的方法引用來表示:
List<Integer> features = Arrays.asList(1,2);features.forEach(System.out::println);方法引用也是java8的新特性,由::操作符標示,詳細可參考方法引用的文章,本文不贅述。
四、函數式接口
在上一節中我們提到:“用lambda表達式代替匿名類的關鍵在于,匿名類實現的接口使用了java.lang.FunctionalInterface注解,且只有一個待實現的抽象接口方法”, 這里的接口便是函數式接口。
函數式接口(Functional Interface)是java8新增的特性,它是一個有且僅有一個抽象方法,但是可以有多個非抽象方法的接口。函數式接口可以被隱式轉換為lambda表達式。
Runnable接口是在JDK1.8之前已經存在的接口,在JDK1.8中加入了@FunctionalInterface注解,表示將其定義為一個函數式接口。在JDK1.8中定義的函數式接口還有:
- 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
JDK1.8新增加的函數式接口有java.util.function包下的接口,典型的如上一節中提到的Consumer接口,感興趣的讀者可以閱讀JDK1.8的源碼,在此不逐個列出,在下一節本文還會列舉java.util.function包中典型的函數式接口的使用。
到這里,可以總結出,java8中用lambda表達式代替匿名內部類,本質上是將接口定義為函數式接口,并將函數式接口隱式轉換為lambda表達式、
五、典型函數式接口的使用
上一節我們理解了java8函數式接口的概念和定義方法,本節再列舉java.util.function幾個典型的函數式接口的使用,加深下函數式接口與lambda表達式結合的理解。
5.1 Predicate接口
5.1.1 Predicate接口的基本用法
Predicate接口適合用于過濾,測試對象是否符合某個條件,Predicate接口源碼如下:
@FunctionalInterface public interface Predicate<T> {boolean test(T t);default Predicate<T> and(Predicate<? super T> other) {Objects.requireNonNull(other);return (t) -> test(t) && other.test(t);}default Predicate<T> negate() {return (t) -> !test(t);}default Predicate<T> or(Predicate<? super T> other) {Objects.requireNonNull(other);return (t) -> test(t) || other.test(t);}static <T> Predicate<T> isEqual(Object targetRef) {return (null == targetRef)? Objects::isNull: object -> targetRef.equals(object);} }可以看到,Predicate接口待實現的唯一抽象方法是 boolean test(T t) 方法。我們用Predicate接口實現從整數型數組中過濾正數:
public static void main(String[] args){ List<Integer> numbers = Arrays.asList(-1, -2, 0, 4, 5);filter(numbers, n -> n > 0);}public static void filter(List<Integer> numbers, Predicate<Integer> condition){for (Integer number : numbers){if (condition.test(number)){System.out.println("Eligible number: " + number);}}}運行結果如下:
Eligible number: 4 Eligible number: 5對數組的迭代,還可以使用Stream API的方式:
public static void main(String[] args){List<Integer> numbers = Arrays.asList(-1, -2, 0, 4, 5);numbers.stream().filter(n -> n > 0).forEach(n -> System.out.println("Eligible number: " + n));}上面的代碼采用Stream API + Predicate接口 + Consumer接口的方式實現了同樣的功能,代碼量大大減少。Stream API(java.util.stream)同樣是java8的新特性,將真正的函數式編程風格引入到java語言中,進一步簡化了代碼。
5.1.2 Predicate接口的進階用法
我們再看上一節提到的Predicate接口的源碼,發現它有三個default關鍵字定義的方法,分別為and()、negate()、or()三個方法,顧名思義,它們類似于邏輯操作&&、!、||,用于生成新的Predicate對象。
以上一節的數組為例,我們用and操作過濾出數組中大于-1且小于5的數字:
public static void main(String[] args){ List<Integer> numbers = Arrays.asList(-1, -2, 0, 4, 5);filter(numbers, n -> n > -1 , n -> n < 5);}public static void filter(List<Integer> numbers, Predicate<Integer> first, Predicate<Integer> second){for (Integer number : numbers){if (first.and(second).test(number)){System.out.println("Eligible number: " + number);}}}結果為:
Eligible number: 0 Eligible number: 4上例用and()方法將兩個Predicate對象進行and運算,同理negate()、or()方法的使用也很簡單,在此不再贅述。
5.2 Stream API
Stream API將處理的數據源看做一種Stream(流),Stream(流)在Pipeline(管道)中傳輸和運算,Stream API結合lambda表達式可以很方便的對集合進行篩選、排序等運算,由于篇幅較大,請閱讀【java8新特性】Stream API詳解,本文中不詳細講解。
5.3 Optional
Optional類是Java8為了解決null值判斷問題,借鑒google guava類庫的Optional類而引入的一個同名Optional類,使用Optional類配合lambda表達式可以避免顯式的null值判斷,并實現很多類似Stream API的功能,由于篇幅較大,請閱讀【java8新特性】Optional詳解,本文中不詳細講解。
五、注意事項
而如果對參數有任何修改時不能使用方法引用,如:
features.forEach(n -> System.out.println(n+1));聯系:
1) 都可以訪問final或effectively final局部變量。
2) 生成的對象都可以調用實現的接口方法。
區別:
1) this指針的指向不同。我們知道匿名類的this指針指向匿名類,而lambda表達式的this指針指向的是包圍lambda表達式的類。
2) 編譯方式不同。lambda在編譯器內部被翻譯為私有方法,并使用了Java 7的 invokedynamic 字節碼指令來動態綁定這個方法
3) 實現的接口限制有區別。匿名類可以為任意接口創建實例,只要實現接口所有的抽象方法即可;而lambda表達式只能實現函數式接口(只有一個必須實現的抽象方法)。
4) 接口默認方法的調用權限不同。匿名類實現的抽象方法允許調用接口中的默認方法,而lambda表達式不能調用接口中的默認方法。
-
【java8新特性】——lambda表達式與函數式接口詳解(一)
-
【java8新特性】——Stream API詳解(二)
-
【java8新特性】——Optional詳解(三)
-
【java8新特性】——方法引用(四)
-
【java8新特性】——默認方法(五)
總結
以上是生活随笔為你收集整理的【java8新特性】——lambda表达式与函数式接口详解(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html游戏禁止微信浏览器下拉,如何用电
- 下一篇: linux tomcat php配置文件