java 8 lambda
lambda
在學習lambda
之前先來看一段程式碼,傳入一個User的集合,返回符合條件的User集合
public static List<User> filter(List<User> users,Predicate<User> predicate) {
List<User> result = new ArrayList<>();
for (User user: users){
if (predicate.test(user)) result.add(user);
}
return result;
}
複製程式碼
程式碼中使用了Predicate
介面,接受一個泛型T,返回一個布林值
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
複製程式碼
java7中,我們可以使用匿名內部類
來作為入參來呼叫filter
方法
public static List<User> filterZhang(List<User> users) {
return filter(users,new Predicate<User>() {
@Override
public boolean test(User user) {
return user.getName().startsWith("張");
}
});
}
複製程式碼
java8中,我們可以使用lambda
來實現
public static List<User> filterWang(List<User> users) {
return filter(users,user -> user.getName().startsWith("王"));
}
複製程式碼
lambda其實是一個箭頭函式
,也可稱為匿名函式
ES6
中的箭頭函式,只不過javascript中使用=>
,而java中使用->
lambda的語法
箭頭操作符將lambda表示式分成了兩部分:
- 左側:lambda表示式的引數列表(介面中
抽象方法
的引數列表) - 右側:lambda表示式中所需執行的功能(lambda體,對
抽象方法
的實現)
上面說到的抽象方法,指的就是Predicate介面中唯一的一個抽象方法 test,接收一個泛型T,返回boolean值
boolean test(T t)
複製程式碼
再看看我們寫的lambda表示式,是不是接受一個泛型為User的user物件,如果user的姓王就返回true,反之返回false,引數和返回值 一一對應
user -> user.getName().startsWith("王")
複製程式碼
所以 lambda箭頭函式必須和Predicate
介面中那個唯一
的抽象方法保持一致(引數和返回值完全相同),正因為如此,lambda中會對引數型別進行型別推斷
, 我們只寫了一個user,java就知道這是一個User物件
語法有如下幾種格式:
- 語法格式一(無引數無返回值):
() -> 具體實現
複製程式碼
- 語法格式二(有一個引數無返回值):
(x) -> 具體實現
//或
x -> 具體實現
複製程式碼
- 語法格式三(有多個引數,有返回值,並且lambda體中有多條語句):
(x,y) -> {具體實現}
複製程式碼
- 語法格式四:若方法體只有一條語句,那麼大括號和return都可以省略
注:lambda表示式的引數列表的引數型別可以省略不寫,可以進行型別推斷
。
函式式介面
什麼是函式式介面?
- 像Runnable和Comparator這樣只有一個抽象方法的介面,稱為函式式介面。 也可以在介面上加上@FunctionalInterface註解,如果編譯通過,則該介面就是函式式介面。
- lambda表示式就是對函式式介面中那個唯一的抽象方法的實現
函式是介面都有哪些?
函式式介面大部分定義在 java.util.function
包中, 且用@FunctionalInterface
註解
看一個需求
需求:需要對兩個數進行加減乘除等運算,怎麼實現?
- 傳統做法:傳統做法中,需要進行幾種運算,我們就要寫幾個方法。一種運算對應一個方法。
public static void main(String[] args) {
add(2,1);
minus(2,1);
multiply(2,1);
divide(2,1);
}
static int add(int left,int right) {
return left + right;
}
static int minus(int left,int right) {
return left - right;
}
static int multiply(int left,int right) {
return left * right;
}
static int divide(int left,int right) {
return left / right;
}
複製程式碼
- lambda做法:首先要定義一個函式式介面,介面中只有一個方法,接收兩個引數。
public static void main(String[] args) {
calc(2,1,(left,right) -> left + right);
calc(2,right) -> left - right);
calc(2,right) -> left * right);
calc(2,right) -> left / right);
}
static int calc(int left,int right,Calculate calculate) {
return calculate.applyAsInt(left,right);
}
@FunctionalInterface
interface Calculate {
int applyAsInt(int left,int right);
}
d
複製程式碼
所以用lambda的話,只需要定義一個函式式介面,不管進行什麼操作,都可以用lambda解決,不用再一種運算對應一個方法。但是,還需要自己定義函式式介面,好像也沒簡單很多。Java考慮到這點了,所以內建了函式式介面,大部分放在java.util.function
包中,介面用@FunctionalInterface
註解。
如Predicate介面
方法引用
- 當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法引用。
- 如果某個方法和 函式式介面中那個唯一的抽象函式保持一致(引數和返回值), 則可以使用雙引號 :: 的方式
- 不過實現抽象方法的引數列表,必須與引用方法的引數列表保持一致。
方法引用語法:
- 物件::例項方法
- 類::靜態方法
- 類::例項方法
在將lambda時舉的例子,可以換成方法引用
的寫法
public static List<User> filterLi(List<User> users) {
return filter(users,Demo1::test);
}
// test 相當於 Predicat介面中抽象方法test的實現
private static boolean test(User user) {
return user.getName().startsWith("李");
}
複製程式碼
方法引用相較於lambda表示式的優點
- 有自己的方法名,要幹什麼一目瞭然
- 因為是一個方法,所以可以有複雜的邏輯程式碼,而在lambda中寫複雜的邏輯程式碼是很不優雅的,可讀性變差