詳解java中的Lambda表達式

Lambda表達式的前世今生(來歷與概述)

Lambda表達式的前世------匿名類

以往,使用單一抽象方法的接口被用作函數類型。 它們的實例表示函數(functions)或行動(actions)。 自從 JDK 1.1 于 1997 年發布以來,創建函數對象的主要手段就是匿名類。匿名類,通俗地講,就是沒有類名,直接通過new關鍵字創建這個類的實例。下面是匿名類的一個例子:

java.util包中的Comparator接口

public interface Comparator<T> {int compare(T o1, T o2);
}

使用匿名類創建排序的比較方法(強制排序順序):

Collections.sort(words, new Comparator<String>() {public int compare(String s1, String s2) {return Integer.compare(s1.length(), s2.length());}
});

匿名類適用于需要函數對象的經典面向對象設計模式,特別是策略模式,上面的匿名類是排序字符串的具體策略。 然而,匿名類確實過于冗長。

Lambda表達式的今生

在 Java 8 中,語言形式化了這樣的概念,即使用單個抽象方法的接口是特別的,應該得到特別的對待。 這些接口現在稱為函數式接口,并且該語言允許你使用lambda 表達式或簡稱 lambda 來創建這些接口的實例。 Lambdas 在功能上與匿名類相似,但更為簡潔。 下面的代碼使用 lambdas 替換上面的匿名類。清晰明了:

Collections.sort(words,(s1, s2) -> Integer.compare(s1.length(), s2.length()));

Lambda表達式語法

JDK1.8之后引入的一種語法,他的寫法是使用一個->符號,箭頭將Lambda表達式分為左右兩部分,左邊寫的是實現的這個接口中的抽象方法中的形參列表,右邊就是對抽象方法的處理

(實現的這個接口中的抽象方法中的形參列表) -> 抽象方法的處理

具體寫法

因為Lambda表達式的核心就是實現的這個接口中的抽象方法中的形參列表) -> 抽象方法的處理,因此根據形參列表與返回值的不同,Lambda表達式的具體寫法也不相同

無返回值有形參的抽象方法

public class MyTest1 {public static void main(String[] args) {MyInterface myInterface = new MyInterface() {@Overridepublic void show(int a, int b) {System.out.println(a + b);}};myInterface.show(20, 30);//50//簡寫1:方法名可以自己推斷出來MyInterface myInterface1 = (int a, int b) -> {System.out.println(a + b);};myInterface1.show(20, 40);//60//簡寫2:可以省略形參列表中的形參類型MyInterface myInterface2 = (a, b) -> {System.out.println(a + b);//70};myInterface2.show(20, 50);//簡寫3:如果抽象方法中只有一行代碼,可以省略方法體的大括號,當然,如果不止一行,就不能省略MyInterface myInterface3 = (a, b) -> System.out.println(a + b);myInterface3.show(20, 60);//80}
}
public interface MyInterface {public abstract void show(int a,int b);
}

主要特點:

  • 可以省略方法名,IDEA會幫你自動檢測方法名;
  • 可以省略方法中的形參類型;
  • 如果對抽象方法的實現邏輯只有一行,可以省略方法體的大括號,當然如果不止一行,就不能省略了;

有返回值的抽象方法

public class MyTest2 {public static void main(String[] args) {MyInterface1 test1 = new MyInterface1() {@Overridepublic int test(int a, int b) {return a - b;}};System.out.println(test1.test(90, 8));//82//簡寫1:MyInterface1 test2 = (int a, int b) -> {return a - b;};System.out.println(test2.test(20, 10));//10//簡寫2:MyInterface1 test3 = (a, b) -> {return a - b;};System.out.println(test3.test(30, 10));//20//簡寫3:這個有返回值的方法,不能直接去掉大括號,還需要去掉return關鍵字MyInterface1 test4 = (a, b) -> a - b;System.out.println(test4.test(40, 10));//30}
}
public interface MyInterface1 {public abstract int test(int a,int b);
}
  • 有返回值的方法,如果要去掉大括號,還需要去掉return關鍵字;

有一個形參的抽象方法

public class MyTest3 {public static void main(String[] args) {MyInterface2 myInterface = a -> a-20;myInterface.show(20);}
}
public interface MyInterface2 {public abstract int show(int a);
}
  • 形參列表中只有一個參數,可以去掉形參的括號

Lambda表達式作為參數傳遞

參數的類型為函數式接口

import java.util.Arrays;
public class MyTest4 {public static void main(String[] args) {Integer[] ints = {89, 67, 23};Arrays.sort(ints, (o1, o2) -> o1-o2);System.out.println(Arrays.toString(ints));//[23, 67, 89]}
}

什么情況下使用

Lambda表達式不是萬能的,他需要函數式接口的支持,只有當某個方法需要傳入的參數類型為函數式接口或者作為方法返回值時,才能使用。

什么是函數式接口:
函數式接口的定義是: 只包含一個抽象方法的接口,稱為函數式接口;
其實我們的Lambda表達式就是對函數式接口的一種簡寫方式,所以只有是函數式接口,我們才能用Lambda表達式;再換句話說,Lambda表達式需要函數式接口的支持,那函數式接口我們可以自己定義,當然JDK1.8也給我們提供了一些現成的函數式接口;

//自定義一個函數式接口
@FunctionalInterface//此注解用來表明這是一個函數式接口
public interface MyInterFace<T> {
//函數式接口只有一個抽象方法
void getValue(T t);
}
//自定義的函數式接口的Lambda表達式
MyInterFace<String> face=(x)->System.out.println(x);
  • 可以通過 Lambda 表達式來創建該接口的對象,我們可以在任意函數式接口上使用@FunctionalInterface 注解,這樣做可以檢查它是否是一個函數式接口;
  • 為什么只能有一個抽象方法,如果有多個抽象方法,這個接口不是函數式接口,簡寫的時候省略了方法名,IDEA不能知道到底重寫的是哪一個方法,不能推斷出來;
  • 注解寫在接口聲明上面,如果不報錯,就不是函數式接口;
  • JDK1.8之后,提供了很多函數式接口,作為參數傳遞;

常用的函數式接口

Java 8在java.util.function包下預定義了大量的函數式接口供我們使用

我們重點學習下面4個接口

  • Supplier接口
  • Consumer接口
  • Predicate接口
  • Function接口

Supplier接口

Supplier< T >:包含一個無參的方法

  • T get():獲得結果
  • 該方法不需要參數,他會按照某種實現邏輯(由Lambda表達式實現)返回一個數據
  • Supplier< T >接口也被稱為生產型接口,如果我們指定了接口的泛型是什么類型,那么接口中的get方法就會產生什么類型的數據供我們使用
import java.util.function.Supplier;public class SupplierDemo {public static void main(String[] args) {String s = getString(()->"張三");int i = getInteger(()->18);System.out.println(s+","+i);}//定義一個方法,返回一個字符串數據private static String getString(Supplier<String> sup){return sup.get();}//定義一個方法,返回一個整數數據private static Integer getInteger(Supplier<Integer> sup){return sup.get();}
}

Consumer接口

Consumer< T >:包含兩個方法

  • void accept(T t):對給定的參數執行此操作
  • default Consumer < T > andThen(Consumer after):返回一個組合的Consumer,依次執行此操作,然后執行after操作
  • Consumer< T >接口也被稱為消費型接口,它消費的數據類型由泛型指定
import java.util.function.Consumer;public class ConsumerDemo {public static void main(String[] args) {operatorString("張三", (s) -> System.out.println(s));operatorString("張三", (s) -> System.out.println(s), (s)-> System.out.println(new StringBuilder(s).reverse().toString()));}//定義一個方法,消費一個字符串數據private static void operatorString(String name, Consumer<String> con) {con.accept(name);}//定義一個方法,用不同的方式消費同一個字符串兩次private static void operatorString(String name, Consumer<String> con1,Consumer<String> con2) {
//        con1.accept(name);
//        con2.accept(name);//返回一個組合的Consumercon1.andThen(con2).accept(name);}
}

Predicate接口

Predicate< T >:常用的四個方法

  • boolean test(T t):對給定的參數進行判斷(判斷邏輯由Lambda表達式實現),返回一個布爾值
  • default Predicate< T > negate():返回一個邏輯的否定,對應邏輯非
  • default Predicate< T > and():返回一個組合判斷,對應短路與
  • default Predicate< T > or():返回一個組合判斷,對應短路或
  • isEqual():測試兩個參數是否相等
  • Predicate< T >:接口通常用于判斷參數是否滿足指定的條件
import java.util.function.Predicate;public class ConsumerTest {public static void main(String[] args) {boolean string = chenkString("張三", s -> s.equals("張三"));System.out.println(string);boolean hello = chenkString("hello", s -> s.length() > 8, s -> s.length() < 18);System.out.println(hello);}//判定給定的字符串是否滿足要求
//    private static boolean chenkString(String s, Predicate<String> pre){
//        return pre.test(s);
//    }private static boolean chenkString(String s, Predicate<String> pre){return pre.negate().test(s);}//    private static boolean chenkString(String s, Predicate<String> pre, Predicate<String> pre1){
//        return pre.and(pre1).test(s);
//    }private static boolean chenkString(String s, Predicate<String> pre, Predicate<String> pre1){return pre.or(pre1).test(s);}
}

Function接口

Runction<T,R>:常用的兩個方法

  • R apply(T t):將此函數應用于給定的參數
  • default< V >:Function andThen(Function after):返回一個組合函數,首先將該函數應用于輸入,然后將after函數應用于結果
  • Function<T,R>:接口通常用于對參數進行處理,轉換(處理邏輯由Lambda表達式實現),然后返回一個新值
import java.util.function.Function;public class ConsumerTest {public static void main(String[] args) {convert("18", s -> Integer.parseInt(s));convert(20, integer -> String.valueOf(integer + 18));convert("245", s -> Integer.parseInt(s), integer -> String.valueOf(integer + 18));}   //定義一個方法,把一個字符串轉換成int類型,在控制臺輸出private static void convert(String s, Function<String, Integer> fun) {int i = fun.apply(s);System.out.println(i);}    //定義一個方法,把int類型數據加上一個整數之后,轉換為字符串在控制臺輸出private static void convert(int i, Function<Integer, String> fun) {String s = fun.apply(i);System.out.println(s);}   //定義一個方法,把一個字符串轉換int類型,把int類型的數據加上一個整數后,轉換成字符串在控制臺輸出private static void convert(String s, Function<String, Integer> fun1, Function<Integer, String> fun2) {String s1 = fun2.apply(fun1.apply(s));System.out.println(s1);}
}

方法引用

先來看一下什么是方法引用:

方法引用其實是Lambda表達式的另一種寫法,當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法引用
注意: 實現抽象方法的參數列表,必須與方法引用方法的參數列表保持一致!
方法引用:使用操作符::將方法名和對象或類的名字分隔開來,三種主要使用情況為:

對象::實例方法
類::靜態方法
類::實例方法

對象::實例方法

import java.util.function.Consumer;public class MyTest {public static void main(String[] args) {Consumer<String> consumer = new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}};consumer.accept("aaaaaaaaaaaaaa");//aaaaaaaaaaaaaa//簡寫1:Consumer<String> consumer1 = (String s) -> {System.out.println(s);};consumer1.accept("abc");//abc//簡寫2:Consumer<String> consumer2 = (s) -> System.out.println(s);consumer2.accept("bcd");//bcd//簡寫3:Consumer<String> consumer3 = System.out::println;consumer3.accept("abc");//abc}
}

為什么可以寫成上述方式?
因為:System.out.println(s);與void accept(String s)一樣,都是使用s作為參數,返回值是void,因此就可以簡寫為簡寫3;

類::靜態方法

import java.util.function.BinaryOperator;public class MyTest1 {public static void main(String[] args) {BinaryOperator<Double> operator = new BinaryOperator<Double>(){@Overridepublic Double apply(Double o, Double o2) {return Math.max(o,o2);}};System.out.println(operator.apply(2.13, 3.12));//3.12BinaryOperator<Double> operator2 = (o, o2) -> Math.max(o,o2);System.out.println(operator2.apply(2.13, 3.12));//3.12BinaryOperator<Double> operator3 = Math::max;Double max = operator3.apply(5.0, 20.0);System.out.println(max);//20.0}
}

因為Math.max()所需要的參數以及返回值與重寫的accpet()一樣,因此可以簡寫為類::靜態方法

下面再舉一個例子

import java.util.Comparator;
public class MyTest2 {public static void main(String[] args) {Comparator<Integer> comparator = new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return Integer.compare(o1,o2);}};System.out.println(comparator.compare(20, 12));//1Comparator<Integer> comparator1 = Integer::compareTo;System.out.println(comparator1.compare(20, 12));//1}
}

類::實例方法

import java.util.Comparator;
public class MyTest2 {public static void main(String[] args) {Comparator<String> comparator = new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {return o1.compareTo(o2);}};System.out.println(comparator.compare("20", "12"));//1Comparator<String> comparator1 = String::compareTo;System.out.println(comparator1.compare("20", "12"));//1}
}

為什么可以這樣寫?、
傳遞過來的兩個參數,一個作為調用者,一個作為參數,這時候,使用類::實例方法簡寫;

構造引用

格式:ClassName::new
與函數式接口相結合,自動與函數式接口中方法兼容。可以把構造器引用賦值給定義的方法,與構造器參數列表要與接口中抽象方法的參數列表一致!

public class MyTest4 {public static void main(String[] args) {Student student = new Student("張三", 23);BiFunction<String, Integer, Student> function = new BiFunction<String, Integer, Student>() {@Overridepublic Student apply(String s, Integer integer) {return student;}};BiFunction<String, Integer, Student> function1 = (s, integer) -> student;BiFunction<String, Integer, Student> function2 = Student::new;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/716947.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/716947.shtml
英文地址,請注明出處:http://en.pswp.cn/news/716947.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【MySQL】超詳細-基礎操作

數據庫定義 數據庫是一類軟件&#xff0c;用來管理數據&#xff0c;組織數據&#xff1b; 關系型數據庫MySQL&#xff08;Oracle,SQL Server,SQLite&#xff09;以表格形式組織數據&#xff0c;數據格式要求嚴格&#xff1b;非關系型數據庫Redis&#xff08;MongoDB,HBase&…

數據結構與算法-冒泡排序

引言 在數據結構與算法的世界里&#xff0c;冒泡排序作為基礎排序算法之一&#xff0c;以其直觀易懂的原理和實現方式&#xff0c;為理解更復雜的數據處理邏輯提供了堅實的入門階梯。盡管在實際應用中由于其效率問題不常被用于大規模數據的排序任務&#xff0c;但它對于每一位初…

【C++】set、multiset與map、multimap的使用

目錄 一、關聯式容器二、鍵值對三、樹形結構的關聯式容器3.1 set3.1.1 模板參數列表3.1.2 構造3.1.3 迭代器3.1.4 容量3.1.5 修改操作 3.2 multiset3.3 map3.3.1 模板參數列表3.3.2 構造3.3.3 迭代器3.3.4 容量3.3.5 修改操作3.3.6 operator[] 3.4 multimap 一、關聯式容器 談…

Hololens 2應用開發系列(1)——使用MRTK在Unity中設置混合現實場景并進行程序模擬

Hololens 2應用開發系列&#xff08;1&#xff09;——使用MRTK在Unity中進行程序模擬 一、前言二、創建和設置MR場景三、MRTK輸入模擬的開啟 一、前言 在前面的文章中&#xff0c;我介紹了Hololens 2開發環境搭建和項目生成部署等相關內容&#xff0c;使我們能生成一個簡單Ho…

Redis 之九:Spring Data Redis -- Redis Template 用法

SpringData Redis Spring Data Redis 是 Spring Data 項目的一部分&#xff0c;它為 Java 應用程序提供了一種便捷的方式來與 Redis 數據庫進行交互。 Spring Data Redis 提供了對 Redis 的抽象封裝&#xff0c;使得開發者能夠以面向對象的方式操作 Redis&#xff0c;并簡化了 …

matlab 寫入格式化文本文件

目錄 一、save函數 二、fprintf函數 matlab 寫入文本文件可以使用save和fprintf函數 save輸出結果: fprintf輸出結果: 1.23, 2.34, 3.45 4.56, 5.67, 6.78 7.89, 8.90, 9.01 可以看出fprintf輸出結果更加人性化,符合要求,下面分別介紹。 一、save函數 …

linux系統Jenkins工具介紹

Jenkins概念介紹 Jenkins概念Jenkins目的特性產品發布流程 Jenkins概念 Jenkins是一個功能強大的應用程序&#xff0c;允許持續集成和持續交付項目&#xff0c;無論用的是什么平臺。這是一個免費的源代碼&#xff0c;可以處理任何類型的構建或持續集成。集成Jenkins可以用于一些…

MQL5-MT5連接上國內期貨

主要原因是昨天在學習MACD時發現給的基礎代碼感覺不對&#xff0c;但無法證明&#xff0c;因為MT5接的都是外匯交易&#xff0c;數據和國內的文華啥的全對不上&#xff0c;便找了一些國內接CTP的&#xff0c;直接寫代碼有點麻煩&#xff0c;雖然之前對接過國內CTP的東西&#x…

AI入門筆記(三)

神經網絡是如何工作的 神經網絡又是如何工作的呢&#xff1f;我們用一個例子來解釋。我們看下面這張圖片&#xff0c;我們要識別出這些圖片都是0并不難&#xff0c;要怎么交給計算機&#xff0c;讓計算機和我們得出同樣的結果&#xff1f;難點就在于模式識別的答案不標準&…

十二、Nacos源碼系列:Nacos配置中心原理(四)- RefreshEvent 事件處理

前面文章&#xff0c;我們說到回調監聽器的方法中&#xff0c;主要就是發布了一個RefreshEvent事件&#xff0c;這個事件主要由 SpringCloud 相關類來處理。今天我們繼續分析后續的流程。 RefreshEvent 事件會由 RefreshEventListener 來處理&#xff0c;該 listener 含有一個 …

Object類方法

toString(): 返回對象的字符串表示形式。默認情況下&#xff0c;返回對象的類名和哈希碼的十六進制表示。 equals(Object obj): 比較兩個對象是否相等。默認情況下&#xff0c;這個方法比較的是兩個對象的引用是否相同&#xff0c;但是通常會在子類中重寫這個方法以實現自定義…

武器大師——操作符詳解(下)

目錄 六、單目操作符 七、逗號表達式 八、下標引用以及函數調用 8.1.下標引用 8.2.函數調用 九、結構體 9.1.結構體 9.1.1結構的聲明 9.1.2結構體的定義和初始化 9.2.結構成員訪問操作符 9.2.1直接訪問 9.2.2間接訪問 十、操作符的屬性 10.1.優先性 10.2.結合性 …

sql基本語法+實驗實踐

sql語法 注釋&#xff1a; 單行 --注釋內容# 注釋內容多行 /* 注釋內容 */數據定義語言DDL 查詢所有數據庫 show databases;注意是databases而不是database。 查詢當前數據庫 select database();創建數據庫 create database [if not exists] 數據庫名 [default charset 字符…

備戰藍橋杯Day22 - 計數排序

計數排序問題描述 對列表進行排序&#xff0c;已知列表中的數范圍都在0-100之間。設計時間復雜度為O(n)的算法。 比如列表中有一串數字&#xff0c;2 5 3 1 6 3 2 1 &#xff0c;需要將他們按照從小到大的次序排列&#xff0c;得到1 1 2 2 3 3 5 6 的結果。那么此時計數排序是…

一:面試流程

面試 項目介紹功能測試接口測試性能測試測試用例 項目介紹 南網智搜是南方電網公司研發的搜索引擎&#xff0c;主要場景Web 端場景有搜索頻道、個人中心、和一些積分活動等&#xff0c;我在里面主要負責功能測試&#xff0c;接口測試&#xff0c;性能測試&#xff0c;壓力測試…

Jetson Xavier NX 開發板Ubuntu18.04 安裝arduino IDE詳細步驟

Jetson 平臺是arch架構&#xff0c;官網上面幾乎都是x86或者arm64的這兩種錯誤版本都存在匹配問題無法使用&#xff0c;不要下載不要下載&#xff01; uname -a #版本查詢1.正確下載打開方式 https://downloads.arduino.cc/arduino-1.8.19-linuxaarch64.tar.xz選擇自己想要下…

LeetCode #104 二叉樹的最大深度

104. 二叉樹的最大深度 題目 二叉樹的 最大深度 是指從根節點到最遠葉子節點的最長路徑上的節點數。 示例 1&#xff1a; 輸入&#xff1a;root [3,9,20,null,null,15,7] 輸出&#xff1a;3 示例 2&#xff1a; 輸入&#xff1a;root [1,null,2] 輸出&#xff1a;2 分析 …

【Godot4自學手冊】第十九節敵人的血量顯示及掉血特效

這一節&#xff0c;我主要學習敵人的血量顯示、掉血顯示和死亡效果。敵人的血量顯示和主人公的血量顯示有所不同&#xff0c;主要是在敵人頭頂有個紅色的血條&#xff0c;受到攻擊敵人的血條會減少&#xff0c;并且有掉血數量的文字顯示&#xff0c;效果如下&#xff1a; 一、…

《中華人民共和國消防法》(2021年修訂版)解讀

單選題&#xff08;共7題&#xff0c;每題5分&#xff09; 1、舉辦大型群眾性活動&#xff0c;承辦人應當依法向&#xff08;&#xff09;申請安全許可。 正確答案&#xff1a;B、公安機關 2、違反消防安全規定進入生產、儲存易燃易爆危險品場所的&#xff0c;情節嚴重的要處…

基于springboot+vue的醫院后臺管理系統

博主主頁&#xff1a;貓頭鷹源碼 博主簡介&#xff1a;Java領域優質創作者、CSDN博客專家、阿里云專家博主、公司架構師、全網粉絲5萬、專注Java技術領域和畢業設計項目實戰&#xff0c;歡迎高校老師\講師\同行交流合作 ?主要內容&#xff1a;畢業設計(Javaweb項目|小程序|Pyt…