日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

还看不懂同事的代码?Lambda 表达式、函数接口了解一下

發布時間:2025/3/16 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 还看不懂同事的代码?Lambda 表达式、函数接口了解一下 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文經授權轉載自微信公眾號:未讀代碼

Java 8?早已經在2014 年 3月 18日發布,毫無疑問?Java 8?對 Java 來說絕對算得上是一次重大版本更新,它包含了十多項語言、庫、工具、JVM 等方面的新特性。

比如提供了語言級的匿名函數,也就是被官方稱為?Lambda?的表達式語法(外界也稱為閉包,Lambda?的引入也讓流式操作成為可能,減少了代碼編寫的復雜性),比如函數式接口,方法引用,重復注解。再比如?Optional?預防空指針,Stearm?流式操作,LocalDateTime?時間操作等。

這一次主要介紹一下 Lambda 的相關情況。

Lambda介紹

Lambda?名字來源于希臘字母表中排序第十一位的字母 λ,大寫為Λ,英語名稱為?Lambda。在 Java 中?Lambda?表達式(lambda expression)是一個匿名函數,在編寫 Java 中的?Lambda?的時候,你也會發現?Lambda?不僅沒有函數名稱,有時候甚至連入參和返回都可以省略,這也讓代碼變得更加緊湊。

上面說了這次是介紹?Lambda?表達式,為什么要介紹函數接口呢?其實 Java 中的函數接口在使用時,可以隱式的轉換成?Lambda?表達式,在?Java 8中已經有很多接口已經聲明為函數接口,如 Runnable、Callable、Comparator 等。

函數接口介紹

函數接口的例子可以看下?Java 8?中的?Runnable?源碼(去掉了注釋)。

package java.lang;
@FunctionalInterfacepublic interface Runnable { public abstract void run();}

那么什么樣子的接口才是函數接口呢?有一個很簡單的定義,也就是只有一個抽象函數的接口,函數接口使用注解?@FunctionalInterface?進行聲明(注解聲明不是必須的,如果沒有注解,也是只有一個抽象函數,依舊會被認為是函數接口)。多一個或者少一個抽象函數都不能定義為函數接口,如果使用了函數接口注解又不止一個抽象函數,那么編譯器會拒絕編譯。函數接口在使用時候可以隱式的轉換成 Lambda 表達式。

Java 8?中很多有很多不同功能的函數接口定義,都放在了?Java 8?新增的?java.util.function包內。下面是一些關于?Java 8?中函數接口功能的描述。

序號接口 & 描述
BiConsumer代表了一個接受兩個輸入參數的操作,并且不返回任何結果
BiFunction代表了一個接受兩個輸入參數的方法,并且返回一個結果
BinaryOperator代表了一個作用于于兩個同類型操作符的操作,并且返回了操作符同類型的結果
BiPredicate代表了一個兩個參數的boolean值方法
BooleanSupplier代表了boolean值結果的提供方
Consumer代表了接受一個輸入參數并且無返回的操作
DoubleBinaryOperator代表了作用于兩個double值操作符的操作,并且返回了一個double值的結果。
DoubleConsumer代表一個接受double值參數的操作,并且不返回結果。
DoubleFunction代表接受一個double值參數的方法,并且返回結果
DoublePredicate代表一個擁有double值參數的boolean值方法
DoubleSupplier代表一個double值結構的提供方
DoubleToIntFunction接受一個double類型輸入,返回一個int類型結果。
DoubleToLongFunction接受一個double類型輸入,返回一個long類型結果
DoubleUnaryOperator接受一個參數同為類型double,返回值類型也為double 。
Function接受一個輸入參數,返回一個結果。
IntBinaryOperator接受兩個參數同為類型int,返回值類型也為int 。
IntConsumer接受一個int類型的輸入參數,無返回值 。
IntFunction接受一個int類型輸入參數,返回一個結果 。
IntPredicate接受一個int輸入參數,返回一個布爾值的結果。
IntSupplier無參數,返回一個int類型結果。
IntToDoubleFunction接受一個int類型輸入,返回一個double類型結果 。
IntToLongFunction接受一個int類型輸入,返回一個long類型結果。
IntUnaryOperator接受一個參數同為類型int,返回值類型也為int 。
LongBinaryOperator接受兩個參數同為類型long,返回值類型也為long。
LongConsumer接受一個long類型的輸入參數,無返回值。
LongFunction接受一個long類型輸入參數,返回一個結果。
LongPredicate接受一個long輸入參數,返回一個布爾值類型結果。
LongSupplier無參數,返回一個結果long類型的值。
LongToDoubleFunction接受一個long類型輸入,返回一個double類型結果。
LongToIntFunction接受一個long類型輸入,返回一個int類型結果。
LongUnaryOperator接受一個參數同為類型long,返回值類型也為long。
ObjDoubleConsumer接受一個object類型和一個double類型的輸入參數,無返回值。
ObjIntConsumer接受一個object類型和一個int類型的輸入參數,無返回值。
ObjLongConsumer接受一個object類型和一個long類型的輸入參數,無返回值。
Predicate接受一個輸入參數,返回一個布爾值結果。
Supplier無參數,返回一個結果。
ToDoubleBiFunction接受兩個輸入參數,返回一個double類型結果
ToDoubleFunction接受一個輸入參數,返回一個double類型結果
ToIntBiFunction接受兩個輸入參數,返回一個int類型結果。
ToIntFunction接受一個輸入參數,返回一個int類型結果。
ToLongBiFunction接受兩個輸入參數,返回一個long類型結果。
ToLongFunction接受一個輸入參數,返回一個long類型結果。
UnaryOperator接受一個參數為類型T,返回值類型也為T。

(上面表格來源于菜鳥教程)

Lambda語法

Lambda 的語法主要是下面幾種。

  • 1.?(params) -> expression

  • 2. (params) -> {statements;}

Lambda 的語法特性。

  • 使用?->?分割 Lambda 參數和處理語句。

  • 類型可選,可以不指定參數類型,編譯器可以自動判斷。

  • 圓括號可選,如果只有一個參數,可以不需要圓括號,多個參數必須要圓括號。

  • 花括號可選,一個語句可以不用花括號,多個參數則花括號必須。

  • 返回值可選,如果只有一個表達式,可以自動返回,不需要 return 語句;花括號中需要 return 語法。
    6. Lambda 中引用的外部變量必須為 final 類型,內部聲明的變量不可修改,內部聲明的變量名稱不能與外部變量名相同。

  • 舉幾個具體的例子, params 在只有一個參數或者沒有參數的時候,可以直接省略不寫,像這樣。

    // 1.不需要參數,沒有返回值,輸出 hello()->System.out.pritnln("hello");
    // 2.不需要參數,返回 hello()->"hello";
    // 3. 接受2個參數(數字),返回兩數之和 (x, y) -> x y // 4. 接受2個數字參數,返回兩數之和 (int x, int y) -> x y // 5. 兩個數字參數,如果都大于10,返回和,如果都小于10,返回差(int x,int y) ->{ if( x > 10 && y > 10){ return x y; } if( x < 10 && y < 10){ return Math.abs(x-y); }};

    通過上面的幾種情況,已經可以大致了解 Lambda 的語法結構了。

    Lambda的使用

    1、對于函數接口

    從上面的介紹中已經知道了 Runnable 接口已經是函數接口了,它可以隱式的轉換為 Lambda 表達式進行使用,通過下面的創建線程并運行的例子看下?Java 8?中 Lambda 表達式的具體使用方式。

    /** * Lambda 的使用,使用 Runnable 例子 * @throws InterruptedException */@Testpublic void createLambda() throws InterruptedException { // 使用 Lambda 之前 Runnable runnable = new Runnable() { @Override public void run() { System.out.println("JDK8 之前的線程創建"); } }; new Thread(runnable).start(); // 使用 Lambda 之后 Runnable runnable1Jdk8 = () -> System.out.println("JDK8 之后的線程創建"); new Thread(runnable1Jdk8).start(); // 更加緊湊的方式 new Thread(() -> System.out.println("JDK8 之后的線程創建")).start();}

    可以發現?Java 8?中的?Lambda?碰到了函數接口 Runnable,自動推斷了要運行的 run 方法,不僅省去了 run 方法的編寫,也代碼變得更加緊湊。

    運行得到結果如下。

    JDK8 之前的線程創建JDK8 之后的線程創建JDK8 之后的線程創建

    上面的 Runnable 函數接口里的 run 方法是沒有參數的情況,如果是有參數的,那么怎么使用呢?我們編寫一個函數接口,寫一個?say?方法接受兩個參數。

    /** * 定義函數接口 */@FunctionalInterfacepublic interface FunctionInterfaceDemo { void say(String name, int age);}

    編寫一個測試類。

    /** * 函數接口,Lambda 測試 */ @Test public void functionLambdaTest() { FunctionInterfaceDemo demo = (name, age) -> System.out.println("我叫" name ",我今年" age "歲了"); demo.say("金庸", 99);?}

    輸出結果。

    我叫金庸,我今年99歲了。

    對于方法引用

    方法引用這個概念前面還沒有介紹過,方法引用可以讓我們直接訪問類的實例或者方法,在 Lambda 只是執行一個方法的時候,就可以不用?Lambda?的編寫方式,而用方法引用的方式:實例/類::方法。這樣不僅代碼更加的緊湊,而且可以增加代碼的可讀性。

    通過一個例子查看方法引用。

    @Getter@Setter@ToString@AllArgsConstructorstatic class User { private String name; private Integer age;}public static List<User> userList = new ArrayList<User>();static { userList.add(new User("A", 26)); userList.add(new User("B", 18)); userList.add(new User("C", 23)); userList.add(new User("D", 19));}/** * 測試方法引用 */@Testpublic void methodRef() { User[] userArr = new User[userList.size()]; userList.toArray(userArr); // User::getAge 調用 getAge 方法 Arrays.sort(userArr, Comparator.comparing(User::getAge)); for (User user : userArr) { System.out.println(user); }}

    得到輸出結果。

    Jdk8Lambda.User(name=B, age=18)Jdk8Lambda.User(name=D, age=19)Jdk8Lambda.User(name=C, age=23)Jdk8Lambda.User(name=A,?age=26)


    2、對于遍歷方式

    Lambda 帶來了新的遍歷方式,Java 8?為集合增加了?foreach?方法,它可以接受函數接口進行操作。下面看一下?Lambda?的集合遍歷方式。

    /** * 新的遍歷方式 */@Testpublic void foreachTest() { List<String> skills = Arrays.asList("java", "golang", "c ", "c", "python"); // 使用 Lambda 之前 for (String skill : skills) { System.out.print(skill ","); } System.out.println(); // 使用 Lambda 之后 // 方式1,forEach lambda skills.forEach((skill) -> System.out.print(skill ",")); System.out.println(); // 方式2,forEach 方法引用 skills.forEach(System.out::print);}

    運行得到輸出。

    java,golang,c ,c,python,java,golang,c ,c,python,javagolangc cpython

    3、對于流式操作

    得益于?Lambda?的引入,讓?Java 8?中的流式操作成為可能,Java 8?提供了 stream 類用于獲取數據流,它專注對數據集合進行各種高效便利操作,提高了編程效率,且同時支持串行和并行的兩種模式匯聚計算。能充分的利用多核優勢。

    流式操作如此強大,?Lambda?在流式操作中怎么使用呢?下面來感受流操作帶來的方便與高效。

    流式操作一切從這里開始。

    // 為集合創建串行流stream()// 為集合創建并行流parallelStream()

    流式操作的去重?distinct和過濾?filter。

    @Testpublic void streamTest() { List<String> skills = Arrays.asList("java", "golang", "c ", "c", "python", "java"); // Jdk8 之前 for (String skill : skills) { System.out.print(skill ","); } System.out.println(); // Jdk8 之后-去重遍歷 skills.stream().distinct().forEach(skill -> System.out.print(skill ",")); System.out.println(); // Jdk8 之后-去重遍歷 skills.stream().distinct().forEach(System.out::print); System.out.println(); // Jdk8 之后-去重,過濾掉 ptyhon 再遍歷 skills.stream().distinct().filter(skill -> skill != "python").forEach(skill -> System.out.print(skill ",")); System.out.println(); // Jdk8 之后轉字符串 String skillString = String.join(",", skills); System.out.println(skillString);}

    運行得到結果。

    java,golang,c ,c,python,java,java,golang,c ,c,python,javagolangc cpythonjava,golang,c ,c,java,golang,c ,c,python,java

    流式操作的數據轉換(也稱映射)map。

    /** * 數據轉換 */ @Test public void mapTest() { List<Integer> numList = Arrays.asList(1, 2, 3, 4, 5); // 數據轉換 numList.stream().map(num -> num * num).forEach(num -> System.out.print(num ","));
    System.out.println();
    // 數據收集 Set<Integer> numSet = numList.stream().map(num -> num * num).collect(Collectors.toSet()); numSet.forEach(num -> System.out.print(num ","));?}

    運行得到結果。

    1,4,9,16,25,16,1,4,9,25,

    流式操作的數學計算。

    /** * 數學計算測試 */@Testpublic void mapMathTest() { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); IntSummaryStatistics stats = list.stream().mapToInt(x -> x).summaryStatistics(); System.out.println("最小值:" stats.getMin()); System.out.println("最大值:" stats.getMax()); System.out.println("個數:" stats.getCount()); System.out.println("和:" stats.getSum()); System.out.println("平均數:" stats.getAverage()); // 求和的另一種方式 Integer integer = list.stream().reduce((sum, cost) -> sum cost).get(); System.out.println(integer);}

    運行得到結果。

    得到輸出最小值:1最大值:5個數:5和:15平均數:3.015

    Lambda總結

    Lamdba?結合函數接口,方法引用,類型推導以及流式操作,可以讓代碼變得更加簡潔緊湊,也可以借此開發出更加強大且支持并行計算的程序,函數編程也為 Java 帶來了新的程序設計方式。但是缺點也很明顯,在實際的使用過程中可能會發現調式困難,測試表示?Lamdba?的遍歷性能并不如 for 的性能高,同事可能沒有學習導致看不懂?Lamdba?等(可以推薦來看這篇文章)。

    有道無術,術可成;有術無道,止于術

    歡迎大家關注Java之道公眾號

    好文章,我在看??

    總結

    以上是生活随笔為你收集整理的还看不懂同事的代码?Lambda 表达式、函数接口了解一下的全部內容,希望文章能夠幫你解決所遇到的問題。

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