更多知識,請移步我的小破站:http://hellofriend.top
1. 個人理解
Lambda 表達式是一種簡潔高效的實現匿名內部類的一種方式,可以使代碼變得更清晰、更靈活,大大增加代碼可讀性。
2. 匿名內部類和Lambda表達式的比較
下面通過實際的例子,將匿名內部類與 Lambda 表達式進行比較,效果一目瞭然。
在 java8 之前使用匿名內部類是這樣的:
public void Test3() {
List<Student> list = fitterStudent(students, new MyPredicate<Student>() {
@Override
public boolean judge(Student stu) {
return stu.getSex().equals("男");
}
});
}
可以看到真正有用的只有return stu.getSex().equals("男");
這一行代碼,但是因爲格式,確不得不把其他的好多行都寫出來,毫無疑問這樣的話,代碼的可讀性會大大降低。
下面來看一下使用Lambda表達式的效果:
public void Test2() {
List<Student> list = fitterStudent(students, (stu) -> stu.getSex().equals("男"));
}
看到效果了吧,原來的6行代碼,現在只需要1行。是不是很厲害…
3. Lambda表達式的基本語法
java8中引入一個新的操作符->
,稱爲箭頭操作符或者爲 lambda 操作符。
箭頭操作符將拉lambda表達式拆分成兩個部分:
左側:參數列表,所實現接口抽象方法的參數列表(函數式接口)
右側:所需執行的功能,稱爲lambda體
lambda表達式需要"函數式接口"(接口中只有一個抽象方法)支持
可以使用註解@FunctionalInterface 幫忙檢查是否是函數式接口
語法格式一: 無參數,無返回值
/**
* @Description 創建一個線程輸出語句
* @Param []
* @return void
**/
@Test
public void test1() {
Runnable r0 = new Runnable() {
@Override
public void run() {
System.out.println("匿名內部類的方式:Hello Lambda!");
}
};
r0.run();
//語法格式一:無參數,無返回值
Runnable r1 = () -> System.out.println("Lambda表達式的方法:Hello Lambda!");
r1.run();
}
語法格式二: 有一個參數,無返回值
/**
* @Description 參數是啥輸出啥
* @Param []
* @return void
**/
@Test
public void test2(){
//聲明一個消費型接口(有一個參數,但是無返回值)調用accept方法,傳入一個參數
Consumer<String> con = (x) -> System.out.println(x);
//語法格式二: 有一個參數,無返回值
con.accept("Hello Lambda!");
}
語法格式三: 若只有一個參數,則參數的小括號可以省略不寫
Consumer<String> con = (x) -> System.out.println(x);
上面的語句等價於:
Consumer<String> con = x -> System.out.println(x);
語法格式四: 有多個參數,lambda體中有多個語句,並且有返回值
/**
* @Description 比較兩個整數
* @Param []
* @return void
**/
@Test
public void test4(){
//用於比較的接口Comparable,提供了一個比較的方法
Comparator<Integer> com = (x, y) -> {
//有多個參數,並且lambda體中有多個語句,必須使用大括號,並且有返回值
System.out.println("x = " + x);
System.out.println("y = " + y);
//Integer類就是Comparator的實現類
return Integer.compare(x,y);
};
System.out.println(com.compare(1, 520));
}
語法格式五: 有多個參數並且有返回值,只有一條語句(return 和大括號都可以省略)
Comparator<Integer> com = (x, y) -> {
return Integer.compare(x,y);
};
上面的寫法等價於:
Comparator<Integer> com = (x,y) -> Integer.compare(x, y);
語法格式六: lambda表達式的參數列表數據類型可以不寫,jvm自動推斷(推薦不寫)
Comparator<Integer> com = (Integer x,Integer y) -> Integer.compare(x, y);
上面的寫法等價於:
Comparator<Integer> com = (x,y) -> Integer.compare(x, y);
小總結
- 左右遇一括號省:
->
左邊參數列表中若只有一個參數,那麼小括號可以省略,->
右邊如果只有一條語句,那麼大括號可以省略。 - 左側推斷類型省:
->
左邊參數的參數類型可以省略。 - 右側一條return省:如果Lambda表達式具有返回值,並且只有一條語句,那麼可以省略
return
關鍵字。
Lambda表達式既然這麼好用,那麼是不是什麼時候都可以應用呢?
不是的,Lambda 表達式必須要有函數式接口的支持,即接口中必須只有一個抽象方法。
4. 四大基本的函數式接口
那麼問題來了,難道每次使用 Lambda 表達式都需要自己寫一個函數式接口嗎?
當然不是了,java8 現在內置了四大基本的函數式接口,而且還有好多函數式接口,所以沒有想象的那麼麻煩,下面就開始介紹 java8 內置的四大函數式接口。
① Consumer 消費型接口(有一個參數,但是無返回值) void accept(T t);
@Test
public void test7() {
happy(20000, (money) -> System.out.println("我今天花了"+money+"元"));
}
/**
* @Description happy的作用就是想參數傳遞給 Consumer
* @Param [money, consumer]
* @return void
**/
public void happy(double money,Consumer<Double> consumer) {
consumer.accept(money);
}
不寫happy方法可以直接這樣寫:
Consumer<Double> consumer = (x) -> {
System.out.println("我今天花了"+x+"元");
};
consumer.accept(2000d);
② Supplier 供給型接口(無參數,但是有返回值) T get();
@Test
public void test8() {
//獲得5個0-100的隨機數
List<Integer> numList = getRandomNum(5, () -> (int)(Math.random()*100));
numList.forEach(System.out::println);
}
/**
* @Description 獲得num個sup.get()得到的結果
* @Param [num, sup]
* @return java.util.List<java.lang.Integer>
**/
public List<Integer> getRandomNum(int num, Supplier<Integer> sup){
List<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
list.add(sup.get());
}
return list;
}
③ Function 函數型接口(有一個參數,有返回值) R apply(T t);
@Test
public void test9() {
String result = strHandler("hello ", (str) -> {
return str+= "Lambda";
});
System.out.println(result);
}
/**
* @Description 將str傳遞給函數型接口
* @Param [str 前綴, fun]
* @return java.lang.String
**/
public String strHandler(String str, Function<String, String> fun) {
return fun.apply(str);
}
④ Predicate 斷言型接口(有一個參數,返回值爲布爾類型) boolean test(T t);
@Test
public void Test10() {
List<String> list = Arrays.asList("Hello","world","my","name","is","lg");
List<String> result = filterStr(list, (str) -> {
//判斷字符串是否含有o
return str.contains("o");
});
result.forEach(System.out::println);
}
/**
* @Description 根據 斷言型接口提供的條件,篩選集合
* @Param [list, pre]
* @return java.util.List<java.lang.String>
**/
public List<String> filterStr(List<String> list, Predicate<String> pre){
List<String> resultList = new ArrayList<>();
for (String string : list) {
if(pre.test(string)) {
resultList.add(string);
}
}
return resultList;
}
5. java8之後的接口可以這麼寫
@FunctionalInterface
interface myInterface{
//只有一個沒實現的方法,這個接口就叫做函數式接口
void fun1();
//默認實現
default int fun2(int a,int b){
return a+b;
}
//靜態方法
static int fun3(int a,int b){
return a*b;
}
}