日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

【java8新特性】——lambda表达式与函数式接口详解(一)

發布時間:2025/3/12 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【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表達式的特性:

  • 可選類型聲明: 無需聲明參數類型,編譯器即可自動識別
  • 可選的參數圓括號: 僅有一個參數時圓括號可以省略
  • 可選的大括號:主體只包含一個語句時可省略大括號
  • 可選的返回關鍵字:主體只包含一個表達式返回值并省略大括號時,編譯器會自動return返回值;有大括號時,需要顯式指定表達式return了一個數值
  • 特性示例:

    //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之前:
  • new Thread(new Runnable(){@Overridepublic void run(){System.out.println("No use lambda.");}}).start();
  • 在java8之后:
  • new Thread(() -> System.out.println("Use lambda")).start();

    可以看到,java8中利用lambda表達式大大簡化了代碼編寫。
    此處簡要提下,用lambda表達式代替匿名類的關鍵在于,匿名類實現的接口使用了java.lang.FunctionalInterface注解,且只有一個待實現的抽象接口方法,如Runnable接口:

    @FunctionalInterfacepublic interface Runnable {public abstract void run();}

    本文后面會詳細講解FunctionalInterface注解。

    3.2 java List迭代的lambda實現
    開發同學經常會使用到集合類,并對集合類對象進行迭代,以實現業務邏輯。
    java8中,集合類的頂層接口java.lang.Iterable定義了一個forEach方法:

    /* @param action The action to be performed for each element* @throws NullPointerException if the specified action is null* @since 1.8*/default void forEach(Consumer<? super T> action) {Objects.requireNonNull(action);for (T t : this) {action.accept(t);}}

    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);for (Integer feature : features) {System.out.println(feature);}
  • 在java8之后:
  • List<Integer> features = Arrays.asList(1,2);features.forEach(n -> System.out.println(n));

    上述邏輯還可以用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詳解,本文中不詳細講解。

    五、注意事項

  • lambda表達式可以使用方法引用,當且僅當主體中不修改lambda表達式提供的參數,如第三章提到的兩種寫法
  • features.forEach(n -> System.out.println(n)); 等價于 features.forEach(System.out::println);

    而如果對參數有任何修改時不能使用方法引用,如:

    features.forEach(n -> System.out.println(n+1));
  • lambda與匿名類的聯系和區別
    聯系:
     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表达式与函数式接口详解(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。