一、概述
1、什么是Lambda表達式
Lambda 表達式是一種匿名函數,簡單地說,它是沒有聲明的方法,也即沒有訪問修飾符、返回值聲明和名字。
它可以寫出更簡潔、更靈活的代碼。作為一種更緊湊的代碼風格,使 Java 語言的表達能力得到了提升。
2、Lambda表達式的語法
基本語法: (parameters) -> expression
或者:(parameters) ->{ statements;
舉例說明:
// 1. 不需要參數,返回值為 5 () -> 5 // 2. 接收一個參數(數字類型),返回其2倍的值 x -> 2 * x // 3. 接受2個參數(數字),並返回他們的差值 (x, y) -> x – y // 4. 接收2個int型整數,返回他們的和 (int x, int y) -> x + y // 5. 接受一個 string 對象,並在控制台打印,不返回任何值(看起來像是返回void) (String s) -> System.out.print(s)
3、什么是函數式接口
再對上面進行舉例說明之前,必須先來理解下函數式接口,因為Lambda是建立在函數式接口的基礎上的。
記住!
(1)只包含一個抽象方法的接口,稱為函數式接口。
(2)你可以通過 Lambda 表達式來創建該接口的對象。
(3)我們可以在任意函數式接口上使用 @FunctionalInterface 注解,這樣做可以檢測它是否是一個函數式接口,同時 javadoc 也會包含一條聲明,說明這個接口是一個函數式接口。
在實際開發者🈶️兩個比較常見的函數式接口:Runnable接口,Comparator接口
先舉例Runnable接口相關
public class Test {
public static void main(String[] args) {
// 1.1使用匿名內部類
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello world !");
}
}).start();
// 1.2使用 lambda 獲得Runnable接口對象
new Thread(() -> System.out.println("Hello world !")).start();
//=============================================================================
// 2.1使用匿名內部類
Runnable race1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello world !");
}
};
// 2.2使用 lambda直接獲得接口對象
Runnable race2 = () -> System.out.println("Hello world !");
// 直接調用 run 方法(沒開新線程哦!)
race1.run();
race2.run();
}
}
/*輸出結果
* Hello world !
* Hello world !
* Hello world !
* Hello world !
*/
通過上面案例可以看出:通過Lambda表達式看去舒服清爽多了,2而通過匿名內部類代碼總是不夠整潔。
再舉一個例子:使用Lambda對數組排序
public class TestArray {
public static void main(String[] args) {
String[] players = {"zhansgan", "lisi", "wangwu", "zhaoliu", "wangmazi"};
// 1.1 使用匿名內部類根據 surname 排序 players
Arrays.sort(players, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return (s1.compareTo(s2));
}
});
// 1.2 使用 lambda 排序,根據 surname
Arrays.sort(players, (String s1, String s2) -> s1.compareTo(s2));
//================================================================================================
// 2.1 使用匿名內部類根據 name lenght 排序 players
Arrays.sort(players, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return (s1.length() - s2.length());
}
});
// 2.2使用Lambda,根據name length
Arrays.sort(players, (String s1, String s2) -> (s1.length() - s2.length()));
//==================================================================================================
// 3.1 使用匿名內部類排序 players, 根據最后一個字母
Arrays.sort(players, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));
}
});
// 3.2 使用Lambda,根據最后一個字母
Arrays.sort(players, (String s1, String s2) -> (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1)));
}
}
通過上面例子我們再來思考為什么Lambda表達式需要函數式接口?其實很簡單目的就是為來保證唯一。
你的Runnable接口只要一個抽象方法,那么我用() -> System.out.println("Hello world !"),就只能代表run方法,如果你下面還有一個抽象方法,那我使用Lambda表達式,那鬼才知道要調用哪個抽象方法呢。
二、方法引用
1、基本介紹
首先注意:方法引用,不是方法調用!方法引用,不是方法調用!方法引用,不是方法調用!
函數式接口的實例可以通過 lambda 表達式、 方法引用、構造方法引用來創建。方法引用是 lambda 表達式的語法糖,任何用方法引用的地方都可由lambda表達式替換,但是並不是所有的lambda表達式都可以用方法引用來替換。
舉例
這就是一個打印集合所有元素的例子,value -> System.out.println(value) 是一個Consumer函數式接口, 這個函數式接口可以通過方法引用來替換。
public class TestArray {
public static void main(String[] args) {
List<String> list = Arrays.asList("xuxiaoxiao", "xudada", "xuzhongzhong");
list.forEach(value -> System.out.println(value));
}
/* 輸出:
* xuxiaoxiao
* xudada
* xuzhongzhong
*/
}
使用方法引用的方式,和上面的輸出是一樣的,方法引用使用的是雙冒號(::)
list.forEach(System.out::println);
2、分類
| 類別 | 使用形式 |
|---|---|
| 靜態方法引用 | 類名 :: 靜態方法名 |
| 實例方法引用 | 對象名(引用名) :: 實例方法名 |
| 類方法引用 | 類名 :: 實例方法名 |
| 構造方法引用 | 類名 :: new |
(1)靜態方法引用
public class Apple {
private String name;
private String color;
private double weight;
public Apple(String name, String color, double weight) {
this.name = name;
this.color = color;
this.weight = weight;
}
public static int compareByWeight(Apple a1, Apple a2) {
double diff = a1.getWeight() - a2.getWeight();
return new Double(diff).intValue();
}
//還有getter setter toString
}
有一個蘋果的List,現在需要根據蘋果的重量進行排序。List 的 sort 函數接收一個 Comparator 類型的參數,Comparator 是一個函數式接口,接收兩個參數,返回一個int值。
Apple的靜態方法compareByWeight正好符合Comparator函數式接口,所以可以使用:
Apple::compareByWeight 靜態方法引用來替代lambda表達式
public class LambdaTest {
public static void main(String[] args) {
Apple apple1 = new Apple("紅富士", "Red", 280);
Apple apple2 = new Apple("馮心", "Yello", 470);
Apple apple3 = new Apple("大牛", "Red", 320);
Apple apple4 = new Apple("小小", "Green", 300);
List<Apple> appleList = Arrays.asList(apple1, apple2, apple3, apple4);
//lambda 表達式形式
//appleList.sort((Apple a1, Apple a2) -> {
// return new Double(a1.getWeight() - a2.getWeight()).intValue();
//});
//靜態方法引用形式(可以看出引用方法比上面的更加簡單
appleList.sort(Apple::compareByWeight);
appleList.forEach(apple -> System.out.println(apple));
}
}
輸出:
Apple{category='紅富士', color='Red', weight=280.0}
Apple{category='小小', color='Green', weight=300.0}
Apple{category='大牛', color='Red', weight=320.0}
Apple{category='馮心', color='Yello', weight=470.0}
注意:Apple.compareByWeight是方法的調用,而Apple::compareByWeight方法引用,這兩者完全不是一回事。
(2)實例方法引用
這個compareByWeight是一個實例方法
public class AppleComparator {
public int compareByWeight(Apple a1, Apple a2) {
double diff = a1.getWeight() - a2.getWeight();
return new Double(diff).intValue();
}
}
下面的例子通過實例對象的方法引用 comparator::compareByWeight 來代替lambda表達式
public class LambdaTest {
public static void main(String[] args) {
Apple apple1 = new Apple("紅富士", "Red", 280);
Apple apple2 = new Apple("馮心", "Yello", 470);
Apple apple3 = new Apple("哈哈", "Red", 320);
Apple apple4 = new Apple("小小", "Green", 300);
List<Apple> appleList = Arrays.asList(apple1, apple2, apple3, apple4);
//lambda 表達式形式
//appleList.sort((Apple a1, Apple a2) -> {
// return new Double(a1.getWeight() - a2.getWeight()).intValue();
//});
//實例方法引用
AppleComparator comparator = new AppleComparator();
appleList.sort(comparator::compareByWeight);
appleList.forEach(apple -> System.out.println(apple));
}
}
輸出:
Apple{category='紅富士', color='Red', weight=280.0}
Apple{category='小小', color='Green', weight=300.0}
Apple{category='哈哈', color='Red', weight=320.0}
Apple{category='馮心', color='Yello', weight=470.0}
通過上面兩個例子可以看到,靜態方法引用和實例方法引用都是比較好理解的。
(3)類方法引用
一般來說,同類型對象的比較,應該當前調用方法的對象與另外一個對象進行比較,好的設計應該像下面:
public class Apple {
private String category;
private String color;
private double weight;
public Apple(String category, String color, double weight) {
this.category = category;
this.color = color;
this.weight = weight;
}
//這里和上面靜態方式唯一區別就是這個參數就一個,需要實例對象調這個方法
public int compareByWeight(Apple other) {
double diff = this.getWeight() - other.getWeight();
return new Double(diff).intValue();
}
//getter setter toString
}
還是之前List排序的例子,看看使用類方法引用如何寫:
public class LambdaTest {
public static void main(String[] args) {
Apple apple1 = new Apple("紅富士", "Red", 280);
Apple apple2 = new Apple("黃元帥", "Yello", 470);
Apple apple3 = new Apple("紅將軍", "Red", 320);
Apple apple4 = new Apple("國光", "Green", 300);
List<Apple> appleList = Arrays.asList(apple1, apple2, apple3, apple4);
//lambda 表達式形式
//appleList.sort((Apple a1, Apple a2) -> {
// return new Double(a1.getWeight() - a2.getWeight()).intValue();
//});
//這里是類方法引用
appleList.sort(Apple::compareByWeight);
appleList.forEach(apple -> System.out.println(apple));
}
}
輸出:
Apple{category='紅富士', color='Red', weight=280.0}
Apple{category='國光', color='Green', weight=300.0}
Apple{category='紅將軍', color='Red', weight=320.0}
Apple{category='黃元帥', color='Yello', weight=470.0}
這里使用的是:類名::實例方法名。首先要說明的是,方法引用不是方法調用。compareByWeight一定是某個實例調用的,就是lambda表達式的第一個參數,然后lambda表達式剩下的參數作為
compareByWeight的參數,這樣compareByWeight正好符合lambda表達式的定義。
或者也可以這樣理解:
(Apple a1, Apple a2) -> { return new Double(a1.getWeight() - a2.getWeight()).intValue(); }
int compareByWeight(Apple other) 需要當前對象調用,然后與另外一個對象比較,並且返回一個int值。可以理解為lambda表達式的第一個參數 a1 賦值給當前對象, 然后 a2 賦值給 other對象,然后返回int值。
(4)構造方法引用
public class ConstructionMethodTest {
public String getString(Supplier<String> supplier) {
return supplier.get();
}
public static void main(String[] args) {
ConstructionMethodTest test = new ConstructionMethodTest();
//lambda表達式形式
System.out.println(test.getString(() -> { return new String();}));
//構造方法引用形式
System.out.println(test.getString(String::new));
}
}
getString 方法接收一個Supplier類型的參數,Supplier 不接收參數,返回一個String。lambda表達式應該這樣寫:
() -> { return new String();}
替換成方法引用的形式如下: 實際上調用的是String 無參構造方法。
String::new
-END-


