# Lambda表達式
[TOC]
## 導學
Lambda表達式是Java 8中的一個新特性,可以用于取代絕大部分的匿名內部類,寫出更優雅的Java代碼。尤其可以用在集合的遍歷與其他一些集合操作中,可以極大的簡化代碼結構。
Lambda 表達式,也可稱為閉包,允許把函數作為一個方法的參數(函數作為參數傳遞進方法中)。
~~~Java
Collections.sort(list,(o1, o2) -> {
double salary1 = o1.getSalary();
double salary2 = o2.getSalary();
if(salary1 > salary2) {
return 1;
} else if(salary1 == salary2) {
return 0;
} else {
return -1;
}
});
~~~
>[info] 在sort方法中,需要傳入兩個參數,第一個是需要排序的集合,第二個排序規則對象。但上述代碼中,排序規則對象被簡化成了一個類似于js中的箭頭函數。此時,形成了將一個方法作為參數傳入到另一個方法中的現象。
## Lambda表達式的使用
### Lambda表達式的使用限制
Lambda表達式主要用于替代接口的實現類,所以對于接口是有要求的。要求該接口中只能有一個被實現的方法,但并不是說接口中只能有一個方法。
例如在jdk 8中,接口中可以存在靜態方法和默認方法,但是如果只存在一個需要被實現的方法,則該接口的實現類就可以用Lambda表達式代替。
~~~Java
@FunctionalInterface
public interface IRun {
void run();
default void jump() {
System.out.println("跳");
}
static void rap() {
System.out.println("rap");
}
}
~~~
>[success]`@FunctionalInterface`用于限制接口中只能有一個用于被實現的方法。它修飾的接口稱之為函數式接口。
### Lambda表達式的基本代碼實現
Lambda雖然用于接口實現類,但從表現形式上來說,更類似于替代接口中唯一需要被實現的那個方法。所以,lambda表達式使用`() -> {}`的形式為基本語法。其中`()`用來描述參數列表,`{}`用來描述方法體,`->`作為lambda表達式的運算符,讀作`goes to`。
先新建幾個接口:
~~~Java
/**
* 無參無返回值
*/
public interface NoReNoPa {
void method();
}
/**
* 單參無返回值
*/
public interface NoReOnePa {
void method(int a);
}
/**
* 多參無返回值
*/
public interface NoReMuPa {
void method(int a, int b);
}
/**
* 無參有返回值
*/
public interface OneReNoPa {
int method();
}
/**
* 單參有返回值
*/
public interface OneReOnePa {
int method(int a);
}
/**
* 多參有返回值
*/
public interface OneReMuPa {
int method(int a, int b);
}
~~~
接下來,看看Lambda的具體使用
~~~java
public class LambdaTest {
public static void main(String[] args) {
//如果使用lambda的話,需要去描述接口中的方法
/*NoReNoPa nrnp = new NoReNoPa() {
@Override
public void method() {
}
};*/
//無參無返回值
NoReNoPa nrnp = () -> {
System.out.println("無參無返回值");
};
nrnp.method();
//單參無返回值
NoReOnePa nrop = (int a) -> {
System.out.println("單參無返回值,參數為:" + a);
};
nrop.method(5);
//多參無返回值
NoReMuPa nrmp = (int a, int b) -> {
System.out.println("多參無返回值,參數為:a=" + a + ",b=" + b);
};
nrmp.method(2, 3);
//無參有返回值
OneReNoPa ornp = () -> {
return 5;
};
System.out.println(ornp.method());
//單參有返回值
OneReOnePa orop = (int x) -> {
return x;
};
System.out.println(orop.method(6));
//多參有返回值
OneReMuPa ormp = (int a, int b) -> {
return a + b;
};
System.out.println(ormp.method(3, 4));
}
}
~~~
### Lambda表達式的簡化操作
在Lambda表達式中,可以遵循一定的簡化規則,對代碼進行簡化操作。
1. 簡化參數類型,可以不寫參數類型,但是必須要求所有的參數都不寫參數類型
~~~Java
//多參無返回值
NoReMuPa nrmp = (a, b) -> {
System.out.println("多參無返回值,參數為:a=" + a + ",b=" + b);
};
nrmp.method(2, 3);
~~~
2. 簡化參數的小括號,如果只有一個參數,則可以省略小括號。無參和多參的情況下,不可省略
~~~Java
//單參無返回值
NoReOnePa nrop = a -> {
System.out.println("單參無返回值,參數為:" + a);
};
nrop.method(5);
~~~
3. 簡化方法體大括號,如果方法體中,只有一條語句。則可以省略方法體大括號
~~~Java
//單參無返回值
NoReOnePa nrop = a -> System.out.println("單參無返回值,參數為:" + a);
nrop.method(5);
~~~
4. 簡化return語句,如果方法體中只有一條語句,并且是`return`語句,則可以省略大括號和`return`關鍵字
~~~Java
//單參有返回值
OneReOnePa orop = x -> x + 1;
System.out.println(orop.method(6));
~~~
### Lambda表達式的應用
如果在方法中有接口類型作為參數,并且該接口中只有一個需要被實現的方法,那么在傳入參數時,可以傳入通過Lambda描述的方法執行體。
~~~Java
public class LambdaTest {
public static void print(OneReMuPa ormp, int a , int b) {
if(ormp.method(a, b) < 10) {
System.out.println("小于10");
} else {
System.out.println("大于10");
}
}
public static void main(String[] args) {
print((a , b) -> a + b, 3, 5);
}
}
~~~
## 方法引用
### 方法引用的介紹
方法引用在Lambda表達式的基礎上,繼續對代碼進行簡化。方法引用可以認為是Lambda表達式的一種特殊形式,Lambda表達式可以讓開發者自定義抽象方法的實現代碼,方法引用則可以讓開發者直接引用已存在的實現方法。
示例:
~~~Java
public class LambdaTest {
public static void print(OneReMuPa ormp, int a , int b) {
if(ormp.method(a, b) < 10) {
System.out.println("小于10");
} else {
System.out.println("大于10");
}
}
public static void main(String[] args) {
//print((a , b) -> ORMP.add(a, b), 3, 5);
print(ORMP::add, 3, 5);//方法引用
}
}
class ORMP {
public static int add(int x, int y) {
return x + y;
}
}
~~~
方法引用使用`::`來表達引用者對方法的指向,方法引用并不是什么很神秘的東西,而是引用別人所寫好的方法,來代替自己要寫的方法體。
**注意:方法引用有幾個限制要求:**
>[danger]
>1. 方法引用也是用來代替接口中抽象方法的實現的,所以該接口必須也是`FunctionalInterface`類型的接口
>2. 在抽象方法的實現中,只會調用其他方法,沒有其余操作
>3. 調用的方法需要和抽象方法,返回值一致,參數列表一致。
### 方法引用的類型
1. 在方法引用中,使用類名引用靜態方法`Class::staticMethod`
示例代碼如介紹部分所示
2. 在方法引用中,使用對象引用成員方法`obj::method`
~~~Java
public class LambdaTest {
public static void print(OneReMuPa ormp, int a , int b) {
if(ormp.method(a, b) < 10) {
System.out.println("小于10");
} else {
System.out.println("大于10");
}
}
public static void main(String[] args) {
print((a, b) -> {
ORMP op = new ORMP();
return op.add(a, b);
}, 3, 5);
ORMP op = new ORMP();
print(op::add, 3, 5);//正確
print(new ORMP()::add, 3,12);//正確
NoReOnePa nrop = System.out::println;
nrop.method(5);//System.out.println(5);
}
}
class ORMP {
public int add(int x, int y) {
return x + y;
}
}
~~~
3. 在方法引用中,有一種特殊的引用,使用類名引用成員方法`Class::method`。引用的方法參數列表和需要實現的方法不一致。但只要需要實現的方法參數類型相同,則可以引用這參數列表不同的方法,而且這時候,反而不可以使用對象名引用方法,需要用類名引用該方法。這是因為在抽象方法的實現中,其中一個參數將作為引用方法的調用者。
~~~Java
public class LambdaTest {
public static void main(String[] args) {
IStudent is = (stu1, stu2) -> stu1.function(stu2);
IStudent is2 = Student::function;//將默認使用第一個參數去引用方法
}
}
interface IStudent {
void method(Student stu1, Student stu2);
}
class Student {
public String name;
public int age;
public void function(Student stu) {
if(this.age > stu.age) {
System.out.println("年齡大");
} else {
System.out.println("小一些");
}
}
}
~~~
4. 在方法引用中,調用構造器的寫法為類名引用new `Class::new`
~~~Java
public class LambdaTest {
public static void main(String[] args) {
ICreate ic = new ICreate() {
@Override
public Student method() {
return new Student();
}
};
ICreate ic1 = () -> new Student();
ICreate ic2 = Student::new;
}
}
interface ICreate {
Student method();
}
class Student {
public String name;
public int age;
}
~~~
5. 在方法引用中,構建數組使用 `Class[]::new`
使用較少,但在使用時,抽象方法必須要有參數指定數組大小