JDK新特性-Lambda表示式的神操作
阿新 • • 發佈:2020-11-07
## 一、Lambda表示式的介紹
- Lambda表示式是 Java8 中最重要的新功能之一。使用 Lambda 表達
式可以替代只有一個抽象函式的介面實現,告別匿名內部類,程式碼看
起來更簡潔易懂。Lambda表示式同時還提升了對集合、框架的迭代、
遍歷、過濾資料的操作。
- lambda表示式可以替代只有一個抽象函式的介面實現,告別匿名內部類,程式碼看起來更簡潔易懂
- lambda表示式同時還提升了對集合、框架的迭代、遍歷、過濾資料的操作
- lambda可以極大的減少程式碼冗餘,同時代碼的可讀性要好過冗長的內部類,匿名類
例如以前我們使用匿名內部類來實現程式碼:
```java
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("running1 .....");
}
};
runnable.run();
```
使用lambda表示式實現更簡潔的程式碼:
```java
Runnable runnable3 = ()-> System.out.println("running2....");
runnable3.run();
```
**lambda表示式語法:**
```java
LambdaParameters -> LambdaBody
```
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201107094149930.png#pic_center)
args -> expr或者(object ... args)-> {函式式介面抽象方法實現邏輯}
1、()引數的個數,根據函式式接口裡面抽象的引數個數來決定,當引數只有一個的時候,()可以省略
2、當expr邏輯非常簡單的時候,{}和return可以省略
**案例說明:**
```java
public static void main(String[] args) throws Exception {
Callable c1 = new Callable() {
@Override
public String call() throws Exception {
return "muxiaonong";
}
};
System.out.println(c1.call());
Callable c2 = ()->{return "muxiaonong2";};
System.out.println(c2.call());
//邏輯很簡單的時候省略 {} 和 return
Callable c3 = ()->"muxiaonong3";
System.out.println(c3.call());
}
```
## 二、Lambda表示式的特點
- 函數語言程式設計
- 引數型別自動推斷
- 程式碼量少,簡潔
## 三、Lambda表示式案例
**實現方式列表:**
```java
()->{}
()->{System.out.println(1);}
()->System.out.println(1)
()->{return 100;}
()->100
()->null
(int x)->{return x+1;}
(int x)->x+1
(x)->x+1
x->x+1
```
案例1:執行緒實現方式:
```java
public static void main(String[] args) {
//匿名內部類方式
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("runing1..........");
}
});
//Lambda表示式方式
new Thread(() -> {System.out.println("runing2.....");}).start();
}
```
案例2:集合遍歷實現方式
```java
public static void main(String[] args) {
List list = Arrays.asList("java","python","scala","javascript");
//普通匿名內部類方式
Collections.sort(list, new Comparator() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});
//Lambda方式
Collections.sort(list,(a,b) -> a.length() - b.length());
list.forEach(System.out::println);
}
```
## 四、Lambda表示式的應用場景
重要的事情說三遍:**任何有函式式介面的地方 * 3**
**什麼是函式式介面:** ```只有一個抽象方法(Object類中的方法除外)的介面是函式式介面```
## 五、Lambda表示式實際應用
#### 5.1 無參實體類模擬
**模擬資料庫連線層:**
```java
@FunctionalInterface
public interface StudentDao {
void insert(Student student);
}
```
實體類
```java
/** @Author mxn
* @Description 學生實體類
* @Date 10:19 2020/11/7
* @Param
* @return
**/
public class Student {
}
```
```java
public static void main(String[] args) {
StudentDao sd1 = new StudentDao() {
@Override
public void insert(Student student) {
System.out.println("插入學生1");
}
};
StudentDao sd2 = (student)->{
System.out.println("student: "+student);
};
StudentDao sd3 = (Student student)-> System.out.println("student3:"+student);
sd1.insert(new Student()); //輸出 插入學生1
sd2.insert(new Student());// 輸出
sd3.insert(new Student());// 輸出
}
```
#### 5.2 有參實體類模擬
實體類
```java
/** @Author mxn
* @Description
* @Date 10:26 2020/11/7
* @Param
* @return
**/
public class Teacher {
}
```
介面模擬層
```java
@FunctionalInterface
public interface TeacherDao {
int get(Teacher teacher);
}
```
實現層
```java
public static void main(String[] args) {
TeacherDao td1 = new TeacherDao() {
@Override
public int get(Teacher teacher) {
return 1;
}
};
TeacherDao td2 = (teacher)->{return 2;};
TeacherDao td3 = (Teacher teacher)->{return 3;};
TeacherDao td4 = (teacher)->4;
TeacherDao td5 = (Teacher teacher)->5;
System.out.println(td1.get(new Teacher()));//輸出 1
System.out.println(td2.get(new Teacher()));//輸出 2
System.out.println(td3.get(new Teacher()));//輸出 3
System.out.println(td4.get(new Teacher()));//輸出 4
System.out.println(td5.get(new Teacher()));//輸出 5
}
```
## 六、函式式介面
```Supplier:```代表一個輸出
```Consumer:```代表一個輸入
```BiConsumer:```代表兩個輸入
```Function:```代表一個輸入,一個輸出(一般輸入和輸出是不同型別的)
```UnaryOperator:```代表一個輸入,一個輸出(輸入和輸出是相同型別的)
```BiFunction:```代表兩個輸入,一個輸出(一般輸入和輸出是不同型別的)
```BinaryOperator:```代表兩個輸入,一個輸出(輸入和輸出是相同型別的)
**在Java中提供了一系列的函式式介面,用來接受後續傳入的邏輯,但是對輸入和輸出有要求**
#### 6.1 Supplier:代表一個輸出
```java
Supplier s1 = ()->{return "muxiaonong";};
Supplier s2 = ()->"muxiaonong2";
System.out.println(s1.get());//輸出 muxiaonong
System.out.println(s2.get());//輸出 muxiaonong2
```
#### 6.2 Consumer:代表一個輸入
```java
Consumer c11 = (str) -> System.out.println(str);
c11.accept("beijing");//輸出 beijing
```
#### 6.3 BiConsumer:代表兩個輸入
```java
BiFunction bf = (a,b)->a.length()+b.length();
System.out.println(bf.apply("大吉大利", "今晚吃雞"));//輸出一個字串長度 8
```
#### 6.4 Function:代表一個輸入,一個輸出
```java
// Function 用來接收後面的函式的實現,規定必須有一個輸入(String)有一個輸出(Integer)
Function f1 = (str)->{return str.length();};
System.out.println(f1.apply("abcdefg"));//輸出長度 7
```
## 七、方法的引用
- 方法引用是用來直接訪問類或者例項的已經存在的方法或者構造方法,方法引用提供了一種引用而不執行方法的方式,如果抽象方法的實現恰好可以使用呼叫另外一個方法來實現,就有可能可以使用方法引用
#### 7.1 方法引用的分類
型別| 語法| 對應的lambda表示式
-------- | ----- | -----
靜態方法引用| 類名::staticMethod | (args) -> 類名.staticMethod(args)
例項方法引用 | inst::instMethod| (args) -> inst.instMethod(args)
物件方法引用| 類名::instMethod| (inst,args) -> 類名.instMethod(args)
構造方法引用| 類名::new| (args) -> new 類名(args)
#### 7.2 靜態方法引用
- **靜態方法引用:** 如果函式式介面的實現恰好可以通過 **呼叫一個靜態方法** 來實現,那麼就可以使用靜態方法引用
```java
/**
* @program: lambda
* @ClassName Test2
* @description:
* @author: muxiaonong
* @create: 2020-10-28 22:15
* @Version 1.0
**/
public class Test2 {
//無參靜態方法
static String put(){
System.out.println("put.....");
return "put";
}
//有參靜態方法
public static void getSize(int size){
System.out.println(size);
}
//有參 有返回值靜態方法
public static String toUpperCase(String str){
return str.toUpperCase();
}
//兩個入參,一個返回值靜態方法
public static Integer getLength(String str,String str2){
return str.length()+str2.length();
}
public static void main(String[] args) {
//無參靜態方法-普通呼叫
System.out.println(put());//輸出put
//無參靜態方法-原生呼叫
Supplier s1 = ()-> Test2.put();
System.out.println(s1.get());//輸出put
//無參靜態方法-靜態方法引用
Supplier s2 = Test2::put;
System.out.println(s2.get());//輸出put
//無參靜態方法-內部類呼叫
Supplier s3 = Fun::hehe;
System.out.println(s3.get()); //輸出hehe
// 有參靜態方法-靜態方法引用
Consumer c1 = Test2::getSize;
Consumer c2 = (size)-> Test2.getSize(size);
c1.accept(123);
c2.accept(111);
//有參有返回值靜態方法
Function f1 = (str)->str.toUpperCase();
Function f2 = (str)-> Test2.toUpperCase(str);
Function f3 = Test2::toUpperCase;
Function f4 = Test2::toUpperCase;
System.out.println(f1.apply("abc"));//輸出 ABC
System.out.println(f2.apply("abc"));//輸出 ABC
System.out.println(f3.apply("abc"));//輸出 ABC
System.out.println(f4.apply("abc"));//輸出 ABC
// 兩個引數 一個返回值 函式式介面
BiFunction bf = (a, b)->a.length()+b.length();
BiFunction bf2 = Test2::getLength;
System.out.println(bf2.apply("abc", "def"));//輸出 6
System.out.println(bf.apply("abc", "def"));//輸出 6
}
//內部類
class Fun {
public static String hehe(){
return "hehe";
}
public static String toUpperCase(String str){
return str.toUpperCase();
}
}
}
```
#### 7.3 例項方法引用
- **例項方法引用:** 如果函式式介面的實現恰好可以通過呼叫一個例項的例項方法來實現,那麼就可以使用例項方法引用
```java
public class Test3 {
//例項無參方法
public String put(){
return "put...";
}
//例項有參方法
public void getSize(int size){
System.out.println("size:"+size);
}
//例項有參有返回值方法
public String toUpperCase(String str){
return str.toUpperCase();
}
public static void main(String[] args) {
//例項無參方法返回-普通呼叫
System.out.println(new Test3().put());//輸出 put...
Supplier s1 = ()->new Test3().put();
Supplier s2 = ()->{return new Test3().put();};
Supplier s3 = new Test3()::put;
System.out.println(s1.get());//輸出 put...
System.out.println(s2.get());//輸出 put...
System.out.println(s3.get());//輸出 put...
//唯一的建立一個test3物件
Test3 test = new Test3();
Consumer c1 = (size)->new Test3().getSize(size);
Consumer c2 = new Test3()::getSize;
Consumer c3 = test::getSize;
c1.accept(123);//輸出 size:123
c2.accept(123);//輸出 size:123
c3.accept(123);//輸出 size:123
Function f1 = (str)->str.toUpperCase();
Function f2 = (str)->test.toUpperCase(str);
Function f3 = new Test3()::toUpperCase;
Function f4 = test::toUpperCase;
System.out.println(f1.apply("abc"));//輸出 ABC
System.out.println(f2.apply("abc"));//輸出 ABC
System.out.println(f3.apply("abc"));//輸出 ABC
System.out.println(f4.apply("abc"));//輸出 ABC
}
}
```
#### 7.4 物件方法引用
- **物件方法引用:** 抽象方法的第一個引數型別剛好是例項方法的型別,抽象方法剩餘的引數恰好可以當做例項方法的引數。如果函式式介面的實現能由上面說的例項方法呼叫來實現的話,那麼就可以使用物件方法引用
```java
/** @Author mxn
* @Description //TODO 物件方法引用
* @Date 14:26 2020/11/7
* @Param
* @return
**/
public class Test4 {
public static void main(String[] args) {
Consumer c1 = (too)->new Too().foo();
c1.accept(new Too());//輸出 foo
Consumer c2 = (Too too) ->new Too2().foo();
c2.accept(new Too());//輸出 foo---too2
Consumer c3 = Too::foo;
c3.accept(new Too());//輸出 foo
BiConsumer bc = (too2,str)->new Too2().show(str);
BiConsumer bc2 = Too2::show;
bc.accept(new Too2(),"abc");
bc2.accept(new Too2(),"def");
BiFunction bf1 = (e,s)->new Exec().test(s);
bf1.apply(new Exec(),"abc");
BiFunction bf2 = Exec::test;
bf2.apply(new Exec(),"def");
}
}
class Exec{
public int test(String name){
return 1;
}
}
class Too{
public Integer fun(String s){
return 1;
}
public void foo(){
System.out.println("foo");
}
}
class Too2{
public Integer fun(String s){
return 1;
}
public void foo(){
System.out.println("foo---too2");
}
public void show(String str){
System.out.println("show ---too2"+str);
}
}
```
#### 7.5 構造方法引用
- **構造方法引用:** 如果函式式介面的實現恰好可以通過呼叫一個類的構造方法來實現,那麼就可以使用構造方法引用
```java
/** @Author mxn
* @Description //TODO 構造方法引用
* @Date 14:27 2020/11/7
* @Param
* @return
**/
public class Test5 {
public static void main(String[] args) {
Supplier s1 = ()->new Person();
s1.get();//輸出 呼叫無參的構造方法
Supplier s2 = Person::new;
s2.get();//輸出 呼叫無參的構造方法
Supplier
- s3 = ArrayList::new;
Supplier