【JDK新特性】一篇搞懂Lambda表達(dá)式 & 函數(shù)式接口
hello,我是小索奇
你問(wèn):JDK新特性(包含Lambda表達(dá)式 & 函數(shù)式接口?)要學(xué)嗎?
我答:當(dāng)然要學(xué)了,如果你以后敲代碼的話,你一定會(huì)用到 and 見到,現(xiàn)在學(xué)會(huì)的話,將來(lái)的你一定會(huì)感謝現(xiàn)在的你~
用起來(lái)你會(huì)發(fā)現(xiàn),哇真的好用!在后期學(xué)習(xí)框架時(shí)你也一定會(huì)遇到
代碼比較枯燥,現(xiàn)在不想看的可收藏備看~
## 概述
Lambda是JDK8的語(yǔ)法糖。它可以對(duì)某些匿名內(nèi)部類的寫法進(jìn)行簡(jiǎn)化,它是函數(shù)式編程的一個(gè)重要體現(xiàn)。讓我們不用關(guān)注什么是對(duì)象,重點(diǎn)關(guān)注我們對(duì)數(shù)據(jù)做了什么操作,可以說(shuō)它Lambda表達(dá)式就是一個(gè)對(duì)象
- Lambda表達(dá)式在C++、C#、Python、Scala等一些語(yǔ)言中也支持,Java支持的還算比較晚的(確實(shí)好用)
- Lambda關(guān)注的是方法的參數(shù)(),還有具體做了什么{}方法體,中間用-> ,凡是確定的東西都可刪除(比如一個(gè)內(nèi)部類,直接確定是它)
## 基本語(yǔ)法
(參數(shù)列表)->{代碼}
拓展__匿名內(nèi)部類
- Runnable是一個(gè)接口
- Runnable只有一個(gè)抽象方法

## LambdaTest代碼演示
```
public class LambdaTest {
? ? @Test
? ? public void test1(){
// Runnable只有一個(gè)抽象方法
? ? ? ? Runnable r1 = new Runnable() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? System.out.println("我愛即興小索奇");
? ? ? ? ? ? }
? ? ? ? };
? ? ? ? r1.run();
? ? ? ? System.out.println("***********************");
//Lambda表達(dá)式
? ? ? ? Runnable r2 = () -> {
? ? ? ? ? ? ? ? System.out.println("我是即興小索奇");
? ? ? ? };
? ? ? ? r2.run();
? ? }
? ? @Test
? ? public void test2(){
? ? ? ? Comparator<Integer> com1 = new Comparator<Integer>() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public int compare(Integer o1, Integer o2) {
? ? ? ? ? ? ? ? return Integer.compare(o1,o2);
? ? ? ? ? ? }
? ? ? ? };
? ? ? ? int compare1 = com1.compare(12,21);
? ? ? ? System.out.println(compare1);
? ? ? ? System.out.println("***********************");
? ? ? ??
? ? ? ? //Lambda表達(dá)式的寫法,箭頭后的{}是可以省略的,參數(shù)類型確定也可以省略
? ? ? ??
? ? ? ? Comparator<Integer> com2 =? (Integer o1, Integer o2) -> Integer.compare(o1,o2);
? ? ? ? //? Comparator<Integer> com2 =? (o1, o2) -> Integer.compare(o1,o2);
? ? ? ? int compare2 = com2.compare(23,21);
? ? ? ? System.out.println(compare2);
? ? ? ? System.out.println("***********************");
? ? ? ? //方法引用
? ? ? ? Comparator<Integer> com3 = Integer :: compare;
? ? ? ? int compare3 = com3.compare(23,21);
? ? ? ? System.out.println(compare3);
? ? }
}
```
## LambdaTest01細(xì)節(jié)演示
```
public class LambdaTest1 {
? ? //語(yǔ)法格式一:無(wú)參,無(wú)返回值
? ? @Test
? ? public void test1(){
? ? ? ? Runnable r1 = new Runnable() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? System.out.println("我是即興小索奇");
? ? ? ? ? ? }
? ? ? ? };
? ? ? ? r1.run();
? ? ? ? System.out.println("***********************");
? ? ? ? Runnable r2 = () -> {
? ? ? ? ? ? System.out.println("即興小索奇運(yùn)行了");
? ? ? ? };
? ? ? ? r2.run();
? ? }
? ? //語(yǔ)法格式二:Lambda 需要一個(gè)參數(shù),但是沒有返回值。
? ? @Test
? ? public void test2(){
? ? ? ? Consumer<String> con = new Consumer<String>() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void accept(String s) {
? ? ? ? ? ? ? ? System.out.println(s);
? ? ? ? ? ? }
? ? ? ? };
? ? ? ? con.accept("小索奇在摸魚!");
? ? ? ? System.out.println("*******************");
? ? ? ? Consumer<String> con1 = (String s) -> {
? ? ? ? ? ? System.out.println(s);
? ? ? ? };
? ? ? ? con1.accept("小索奇正在打怪中...");
? ? }
? ? //語(yǔ)法格式三:數(shù)據(jù)類型可以省略,因?yàn)榭捎删幾g器推斷得出,稱為“類型推斷”
? ? @Test
? ? public void test3(){
? ? ? ? Consumer<String> con1 = (String s) -> {
? ? ? ? ? ? System.out.println(s);
? ? ? ? };
? ? ? ? con1.accept("小索奇正在探索中...");
? ? ? ? System.out.println("*******************");
? ? ? ? Consumer<String> con2 = (s) -> {
? ? ? ? ? ? System.out.println(s);
? ? ? ? };
? ? ? ? con1.accept("小索奇正在探索中...");
? ? }
? ? @Test
? ? public void test3_1(){
? ? ? ? int[] arr = {1,2,3,4}; //類型推斷
? ? ? ? HashMap<String,Integer> map = new HashMap<>();//類型推斷
? ? ? ? var entrySet = map.entrySet(); //類型推斷 ,在jdk10及之后可以用,現(xiàn)持續(xù)存在
? ? }
? ? //語(yǔ)法格式四:Lambda 若只需要一個(gè)參數(shù)時(shí),參數(shù)的小括號(hào)可以省略
? ? @Test
? ? public void test4(){
? ? ? ? Consumer<String> con1 = (s) -> {
? ? ? ? ? ? System.out.println(s);
? ? ? ? };
? ? ? ? con1.accept("小索奇正在探索中...");
? ? ? ? System.out.println("*******************");
? ? ? ? Consumer<String> con2 = s -> {
? ? ? ? ? ? System.out.println(s);
? ? ? ? };
? ? ? ? con2.accept("小索奇正在睡覺...");
? ? }
? ? //語(yǔ)法格式五:Lambda 需要兩個(gè)或以上的參數(shù),多條執(zhí)行語(yǔ)句,并且可以有返回值
? ? @Test
? ? public void test5(){
? ? ? ? Comparator<Integer> com1 = new Comparator<Integer>() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public int compare(Integer o1, Integer o2) {
? ? ? ? ? ? ? ? System.out.println(o1);
? ? ? ? ? ? ? ? System.out.println(o2);
? ? ? ? ? ? ? ? return o1.compareTo(o2);
? ? ? ? ? ? }
? ? ? ? };
? ? ? ? System.out.println(com1.compare(12,21));
? ? ? ? System.out.println("*****************************");
? ? ? ? Comparator<Integer> com2 = (o1, o2) -> {
? ? ? ? ? ? System.out.println(o1);
? ? ? ? ? ? System.out.println(o2);
? ? ? ? ? ? return o1.compareTo(o2);
? ? ? ? };
? ? ? ? System.out.println(com2.compare(12,21));
? ? }
? ? //語(yǔ)法格式六:當(dāng) Lambda 體只有一條語(yǔ)句時(shí),return 與大括號(hào)若有,都可以省略
? ? @Test
? ? public void test6(){
? ? ? ? Comparator<Integer> com1 = (o1,o2) -> {
? ? ? ? ? ? return o1.compareTo(o2);
? ? ? ? };
? ? ? ? System.out.println(com1.compare(12,6));
? ? ? ? System.out.println("*****************************");
? ? ? ? Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);
? ? ? ? System.out.println(com2.compare(12,16));
? ? }
? ? @Test
? ? public void test7(){
? ? ? ? Consumer<String> con1 = s -> {
? ? ? ? ? ? System.out.println(s);
? ? ? ? };
? ? ? ? con1.accept("小索奇正在睡大街...");
? ? ? ? System.out.println("*****************************");
? ? }
}
```
## 自定義函數(shù)式接口
```
@FunctionalInterface
public interface MyFunctionalInterface {
? ? void method();
//? ? void method1();
}
public class MyFunctionalInterfaceTest {
? ? @Test
? ? public void test1(){
? ? ? ? MyFunctionalInterface m = () -> System.out.println("hello");
? ? ? ? m.method();
? ? }
}
```
## 總結(jié)
直達(dá) `java.util.function`文檔:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/function/package-summary.html
1. **Lambda表達(dá)式的使用舉例:**
(o1, o2) -> Integer.compare(o1,o2);
2. **Lambda表達(dá)式的格式舉例:**
lambda形參列表 -> lambda體
3. **Lambda表達(dá)式的格式**
-? lambda操作符或箭頭操作符
-? -> 的左邊: lambda形參列表,對(duì)應(yīng)著要重寫的接口中的抽象方法的形參列表。
-? -> 的右邊: lambda體,對(duì)應(yīng)著接口的實(shí)現(xiàn)類要重寫的方法的方法體。
4. **Lambda表達(dá)式的本質(zhì):**
- 一方面,lambda表達(dá)式作為接口的實(shí)現(xiàn)類的對(duì)象。? --->說(shuō)白了Lambda表達(dá)式就是對(duì)象, "萬(wàn)事萬(wàn)物皆對(duì)象"?
? 另一方面,lambda表達(dá)式是一個(gè)匿名函數(shù)。
5. **函數(shù)式接口**
> 什么是函數(shù)式接口?為什么需要函數(shù)式接口?
- 如果接口中只聲明有一個(gè)抽象方法,則此接口就稱為函數(shù)式接口(如Runnable就是函數(shù)式接口 ,底層加上了`@FunctionalInterface`注解,可以校驗(yàn)它是一個(gè)函數(shù)式接口,加了最多就只能寫一個(gè)抽象方法了)
- 因?yàn)橹挥薪o函數(shù)式接口提供實(shí)現(xiàn)類的對(duì)象時(shí),我們才可以使用lambda表達(dá)式。
> api中函數(shù)式接口所在的包
- jdk8中聲明的函數(shù)式接口都在java.util.function包下。
> 4個(gè)基本的函數(shù)式接口
? ? ? ? ? 接口? ? ? ? ? ? 對(duì)應(yīng)的抽象方法
- 消費(fèi)型接口:Consumer<T>? ? ?void accept(T t)
? 消費(fèi)型接口表示接收一個(gè)參數(shù)并執(zhí)行某些操作,但沒有返回值。它常用于需要執(zhí)行一些操作但不需要返回結(jié)果的場(chǎng)景,例如遍歷集合、修改對(duì)象狀態(tài)等,例如
? ```java
? Consumer<String> consumer = s -> System.out.println(s);
? consumer.accept("hello world");
? ```
- 供給型接口:Supplier<T>? ? ?T get()
? 供給型接口表示不需要接收任何參數(shù),但需要返回一個(gè)結(jié)果。它常用于需要生成某些數(shù)據(jù)的場(chǎng)景,例如生成隨機(jī)數(shù)、獲取當(dāng)前時(shí)間等,例如
? ```java
? Supplier<Integer> supplier = () -> (int) (Math.random() * 100);
? System.out.println(supplier.get());
? ```
- 函數(shù)型接口:Function<T,R>? ?R apply(T t)
? 函數(shù)型接口表示接收一個(gè)參數(shù)并返回一個(gè)結(jié)果。它常用于需要對(duì)某些數(shù)據(jù)進(jìn)行處理并返回結(jié)果的場(chǎng)景,例如字符串轉(zhuǎn)換、數(shù)據(jù)加工等。例如:
? ```java
? Function<String, Integer> function = s -> s.length();
? System.out.println(function.apply("hello world"));
? ```
- 判斷型接口:Predicate<T>? ? boolean test(T t)
? 判斷型接口表示接收一個(gè)參數(shù)并返回一個(gè)布爾值。它常用于需要對(duì)某些數(shù)據(jù)進(jìn)行判斷并返回布爾值的場(chǎng)景,例如過(guò)濾集合、判斷對(duì)象狀態(tài)等,例如
? ```java
? Predicate<Integer> predicate = i -> i > 0;
? System.out.println(predicate.test(-1));
? ```
6. **Lambda表達(dá)式的語(yǔ)法規(guī)則總結(jié)**
- -> 的左邊:lambda形參列表,參數(shù)的類型都可以省略。如果形參只有一個(gè),則一對(duì)()也可以省略。
- -> 的右邊:lambda體,對(duì)應(yīng)著重寫的方法的方法體。如果方法體中只有一行執(zhí)行語(yǔ)句,則一對(duì){}可以省略。
? 如果有return關(guān)鍵字,則必須一并省略