詳細解讀Java的Lambda表達式
Lambda 表達式
最早接觸到 Lambda 表達式的時候,是在學(xué)習(xí) python 的時候,當(dāng)時就很好奇。后來,才發(fā)現(xiàn) Java 也有這個方面的知識,最近看了相關(guān)的知識,特定來總結(jié)一下。
Lambada 簡介
lambda 表達式 是Java 8新加入的新特性,它在Java中是引入了函數(shù)式編程這一概念。那么什么是函數(shù)式編程呢?
函數(shù)式編程:函數(shù)式編程是面向數(shù)學(xué)的抽象,將計算描述為一種表達式求值。
我們平常所說的面向?qū)ο缶幊虒儆诿钍骄幊?/strong>,函數(shù)式編程和命令式編程的區(qū)別是:
- 函數(shù)式編程關(guān)心數(shù)據(jù)的映射,命令式編程關(guān)系解決問題的步驟。
- 函數(shù)式編程關(guān)系類型(代數(shù)結(jié)構(gòu))之間的關(guān)系,命令式編程關(guān)系解決問題的步驟。
函數(shù)式編程的本質(zhì):
函數(shù)式編程中的函數(shù)指的不是計算機中的函數(shù),而是數(shù)學(xué)中的函數(shù),即自變量的映射。即:一個函數(shù)的值僅取決于函數(shù)參數(shù)的值,不依賴其他狀態(tài)。
嚴格意義上的函數(shù)式編程意味著不使用可變的變量,賦值,循環(huán)和其他命令式控制結(jié)構(gòu)進行編程。
函數(shù)式編程的好處:
函數(shù)式編程的好處是主要是不可變性帶來的。沒有可變的狀態(tài),函數(shù)就是引用透明(Referential transparency)的和沒有副作用的(No Side Effect)。
上面這些都是一些基本的概念,但是我們平時可能接觸這些方面的東西比較少,所以一開始感覺函數(shù)式編程是很難得東西。
簡單的示例
Talk is cheap, show me the code!
先來一個最簡單的例子,可能也是介紹的最多的例子了。哈哈!
給按鈕添加監(jiān)視器。
使用匿名內(nèi)部類的方式,進行添加。
submit.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "點擊了確定按鈕", "確定", JOptionPane.INFORMATION_MESSAGE);
}
});
這種方式的缺點:使用了很多的模板代碼,真正必要的代碼,只是方法體內(nèi)的代碼。所以,Java 8 引入的 Lambda 表達式可以簡化這種代碼(當(dāng)然了,也是有限制的,不是所有的匿名內(nèi)部類都可以,這個后面會提到。)。
使用 Lambda 表達式 簡化代碼
submit.addActionListener((e)->{
JOptionPane.showMessageDialog(null, "點擊了確定按鈕", "確定", JOptionPane.INFORMATION_MESSAGE);
});
Lambda 表達式是一個匿名方法,將行為像數(shù)據(jù)一樣進行傳遞。
說明
可以看出來,使用 Lambda 表達式簡化后的代碼表達變得更加清晰了,并且不用再去寫繁瑣的模板代碼了。
進一步簡化
參數(shù)括號和代碼體的花括號也可以省略(只有一個參數(shù)時,可以省略圓括號,只有一行代碼時,可以省略花括號)。
ActionListener listener = e->JOptionPane.showMessageDialog(null, "點擊了確定按鈕", "確定", JOptionPane.INFORMATION_MESSAGE);
小結(jié)
當(dāng)使用 Lambda 表達式代替匿名內(nèi)部類創(chuàng)建對象時,Lambda 表達式的代碼塊將會替代實現(xiàn)抽象方法的方法體,Lambda 就相當(dāng)于一個匿名方法。
Lambda 表達式的組成部分
lambda 表達式由三部分組成:
- 形參列表。形參列表允許省略形參類型。如果形參列表中只有一個參數(shù),可以省略形參列表的圓括號。
- 箭頭(->)。英文短線和大于號。
- 代碼塊。如果代碼塊只有一句,可以省略花括號。如果只有一條
return語句,可以省略return,lambda表達式會自動返回這條語句的值。
注:
之所以可以省略形參列表是因為 編譯器 可以進行類型推斷,例如:
List<Dog> dogs1 = new ArrayList<Dog>(); List<Dog> dogs2 = new ArrayList<>();
上面使用 菱形語法,可以省略尖括號里面的東西,這就是類型推斷的作用。
但是類型推斷也不是萬能的,不是所有的都可以推斷出來的,所以有時候,還是要顯示的添加形參類型,例如:
先不要管這個代碼的具體作用。
BinaryOperator b = (x, y)->x*y; //上面這句代碼無法通過編譯,下面是報錯信息:無法將 * 運算符作用于 java.lang.Object 類型。 The operator * is undefined for the argument type(s) java.lang.Object, java.lang.Object
//添加參數(shù)類型,正確的代碼。 BinaryOperator<Integer> b = (x, y)->x*y;
所以,類型推斷不是萬能的,如果編譯器無法推斷,那就是我們的錯誤,不要過度依賴編譯器。有時候,顯示的添加參數(shù)類型,還是很必要的,當(dāng)然了,這需要去多練習(xí)。
函數(shù)式接口
前面了解了,Lambda 表達式可以代替匿名內(nèi)部類,進而達到簡化代碼,表達清晰的目的。那么使用 Lambda 表示式的前提是什么呢?-- 函數(shù)式接口
Lambda 表達式的類型,也被稱為 目標類型 (Target Type),它必須是一個函數(shù)式接口(Functional Interface)。所謂函數(shù)式接口,指的就是:只包含一個抽象方法的接口。(可以包含多個默認方法,靜態(tài)方法,但必須只有一個抽象方法)。
注:Java 8 專門提供了一個注解:@FunctionalInterface。用于標注某個接口是函數(shù)式接口,這樣編譯時就會檢查,如果該接口含有多個抽象方法,編譯器就會報錯。
上面使用 Lambda 表達式來為按鈕添加了監(jiān)視器,可以看出來,Lambda 表達式 代替了 new ActionListener()對象。
所以 Lambda 的表達式就是被當(dāng)成一個對象。
例如:
ActionListener listener = e->JOptionPane.showMessageDialog(null, "點擊了確定按鈕", "確定", JOptionPane.INFORMATION_MESSAGE);
從上面這個例子中可以看出來,Lambda 表達式實現(xiàn)的是匿名方法–因此它只能實現(xiàn)特定函數(shù)式接口中的唯一方法。
所以 Lambda 表達式有下面兩種限制:
Lambda 表達式的目標類型必須是明確的函數(shù)式接口。 Lambda 表達式只能為函數(shù)式接口創(chuàng)建對象。Lambda只能實現(xiàn)一個方法,因此它只能為含有一個抽象方法的接口(函數(shù)式接口)創(chuàng)建對象。
介紹幾個 Java 中重要的函數(shù)接口

從這種表可以看出來,抽象方法的名字反而不是最重要的了,重要的是參數(shù)和返回值。 因為在寫 Lambda 表達式的時候,也不要使用 抽象方法名了。
上面使用幾個簡單的例子來說明上面接口的應(yīng)用:
測試代碼
import java.text.ParseException;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;
public class Test {
public static void main(String[] args) throws ParseException {
//Lambda 表達式中的構(gòu)造器引用,用于簡化代碼。
Creat<Dog> c = Dog::new;
Dog dog = c.creat("小黑", 15);
System.out.println(dog.toString());
Predicate<String> predicate = (words)->{
return words.length() > 20;
};
assert predicate.test("I love you yesterday and today!") : "長度小于20";
assert !predicate.test("God bless you!") : "長度小于20";
System.out.println("------------------------");
Consumer<Dog> consumer = System.out::println;
consumer.accept(dog);
System.out.println("------------------------");
Function<Dog, String> function = (dogObj)->{
return dogObj.getName();
};
System.out.println(function.apply(dog));
System.out.println("------------------------");
Supplier<Dog> supplier = ()->{
return new Dog("大黃", 4);
};
System.out.println(supplier.get());
//一元操作符
UnaryOperator<Boolean> unaryOperation = (flag)->{
return !flag;
};
System.out.println(unaryOperation.apply(true));
BinaryOperator<Integer> binaryOperator = (x, y)->x*y;
int result = binaryOperator.apply(999, 9999);
System.out.println(result);
}
}
測試使用的實體類
public class Dog {
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Dog [name=" + name + ", age=" + age + "]";
}
}
自定義函數(shù)式接口
@FunctionalInterface
public interface Creat<T> {
public T creat(String name, int age);
}
運行截圖就不放了,感興趣的可以試一下。
說明
我這里直接使用 Lambda 創(chuàng)建了對象,然后調(diào)用了這個對象的方法(就是lambda 的代碼塊部分),真正使用的時候,都是直接傳遞 Lambda 表達式的,這種方法并不推薦,但是可以讓我們很好的理解為什么? 可以看出來,Lambda 表達式的作用,最后還是需要調(diào)用 重寫的抽象方法的,只不過使用表達更加清晰,簡化了代碼。
例如:
List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog("大黃", 2));
dogs.add(new Dog("小黑", 3));
dogs.add(new Dog("小哈",1));
//將行為像數(shù)據(jù)一樣傳遞,使用集合的 forEach 方法來遍歷集合,
//參數(shù)可以是一個 Lambda 表達式。
Consumer<? super Dog> con = (e)->{
System.out.println(e);
};
dogs.forEach(con);
System.out.println("--------------------------\n");
//直接傳遞 Lambda 表達式,更加簡潔
dogs.forEach(e->System.out.println(e));
System.out.println("--------------------------\n");
//使用方法引用,進一步簡化(可以看我的另一篇關(guān)于方法引用的博客)
dogs.forEach(System.out::println);
System.out.println("--------------------------\n");
//使用 Lambda 對集合進行定制排序,按照年齡排序(從小到大)。
dogs.sort((e1, e2)->e1.getAge()-e2.getAge());
dogs.forEach(System.out::println);
可以看出來,通過使用 Lambda 表達式可以,極大的簡化代碼,更加方便的操作集合。值得一提的是:Lambda 表達式 和 Stream 的結(jié)合,可以擁有更加豐富的操作,這也是下一步學(xué)習(xí)的方向。
運行截圖:

最后說一下
這個博客介紹了一些 Lambda 表達式的基本知識,但是描述的還是不是很清楚,哈哈!但是,希望通過這個簡單的博客,可以幫助了解一些關(guān)于 Lambda 表達式的基本知識,這些都是很簡單的東西,太難的部分,我可能暫時還沒有觸及到。
Lambda 表達式是一個匿名方法,將行為像數(shù)據(jù)一樣進行傳遞。
這句話很關(guān)鍵,這是對 Lambda 的一個很好的總結(jié)。
到此這篇關(guān)于詳細解讀Java的Lambda表達式的文章就介紹到這了,更多相關(guān)Java Lambda表達式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java中同類對象之間的compareTo()和compare()方法對比分析
這篇文章主要介紹了java中同類對象之間的compareTo()和compare()方法對比分析,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09
spring boot aop 記錄方法執(zhí)行時間代碼示例
這篇文章主要介紹了spring boot aop 記錄方法執(zhí)行時間代碼示例,分享了相關(guān)代碼,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下2018-02-02
Android 單例模式 Singleton 簡單實例設(shè)計模式解析
這篇文章主要介紹了單例模式 Singleton 簡單實例設(shè)計模式解析的相關(guān)資料,需要的朋友可以參考下2016-12-12
詳解用Spring Boot Admin來監(jiān)控我們的微服務(wù)
這篇文章主要介紹了用Spring Boot Admin來監(jiān)控我們的微服務(wù),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
java 集合并發(fā)操作出現(xiàn)的異常ConcurrentModificationException
Map在遍歷時候通常 現(xiàn)獲得其鍵值的集合Set,然后用迭代器Iterator來對Map進行遍歷。2009-06-06
httpclient staleConnectionCheckEnabled獲取連接流程解析
這篇文章主要為大家介紹了httpclient staleConnectionCheckEnabled獲取連接流程示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11
SpringBoot?@DS注解實現(xiàn)多數(shù)據(jù)源配置以及問題解決辦法
這篇文章主要給大家介紹了關(guān)于SpringBoot?@DS注解實現(xiàn)多數(shù)據(jù)源配置以及問題解決辦法,所謂多數(shù)據(jù)源就是一個Java EE項目中采用了不同數(shù)據(jù)庫實例中的多個庫,或者是同一個數(shù)據(jù)庫實例中的多個不同庫,需要的朋友可以參考下2023-11-11

