概述
lambda表达式是JDK 1.8提供的一种新特性,它使得Java也能像C#和C++语言一样进行简单的“函数式编程”,这不仅简化了某些通用结构的实现方式,也大大增强了Java语言的表达功能。
lambda表达式是基于数学中的λ演算得名,本质上就是一个没有方法名的匿名方法。
lambda表达式的使用
lambda表达式不是独立执行的,而是经常被应用在函数式接口(functional interface)定义的抽象方法的实现中。
函数式接口
函数式接口是指只包含一个抽象方法的接口。函数式接口的抽象方法指明了接口的目标用途。
定义了函数式接口之后,就可以把lambda表达式赋值给该接口的一个引用,lambda表达式定义了函数式接口声明的抽象方法的行为。
例如:定义一个接口Computer,其中只有一个抽象方法compute(),则Computer是一个函数式接口。

1 interface Computer {
2
3 int compute(int x, int y);
4
5 }
lambda表达式格式
lambda表达式类似于匿名内部类,表达式本身实现了函数式接口的唯一抽象方法,而表达式返回的结果是一个函数式接口类型。
lambda表达式通常以“(参数) -> {方法体}”这样的格式书写,其中“->”被称为lambda运算符或箭头运算符,具体有2种形式:
省略参数类型:(参数1, 参数2, ...) -> {方法体}
指定参数类型:(类型1 参数1, 类型2 参数2, ...) -> {方法体}
如果方法体内只有一个语句,且该语句的返回值类型与抽象方法定义的返回值类型相同,则可以省略“{ }”和return关键字。

1 @Test
2 void testLambda() {
3 Computer add = (x, y) -> x + y;
4 /***********************************
5 等同于:
6 Computer add = new Computer() {
7
8 @Override
9 public int compute(int x, int y) {
10 return x + y;
11 }
12
13 };
14 ***********************************/
15 }
需要注意的是,如果方法体内只有一个语句,而该语句的返回值类型与抽象方法定义的返回值类型不同,则不能省略“{ }”和return关键字。
如果将compute()方法返回值类型改为void,则上面的代码会报错,提示void类型方法不能返回一个值。
如果方法体内有多个语句,则需要使用“{ }”括起来。

1 @Test
2 void testLambda() {
3 Computer add = (x, y) -> {
4 int result = x + y;
5 return result;
6 };
7 /***********************************
8 等同于:
9 Computer add = new Computer() {
10
11 @Override
12 public int compute(int x, int y) {
13 int result = x + y;
14 return result;
15 }
16
17 };
18 ***********************************/
19 }
示例
传统用法:

1 @Test
2 void testTraditional() {
3 Computer add = new Computer() {
4
5 @Override
6 public int compute(int x, int y) {
7 return x + y;
8 }
9
10 };
11 System.out.println("加法计算器:");
12 System.out.println("90+15=" + add.compute(90, 15));
13 }
lambda表达式用法:

1 @Test
2 void testLambda() {
3 Computer add = (x, y) -> x + y;
4 System.out.println("加法计算器:");
5 System.out.println("90+15=" + add.compute(90, 15));
6 }
输出结果:

方法引用
方法引用是lambda表达式的另一种更为简洁的用法。需要注意的是,引用的方法是当前类可以访问到的方法。方法引用分为3种形式:
类方法的方法引用
类方法的方法引用的格式为:引用类的类名 :: 引用类的类方法名。该方式需要抽象方法的参数与引用的类方法的参数一致,返回值类型也要一致。
定义一个Computer函数式接口,该接口中有一个抽象方法compute()。

1 interface Computer {
2
3 int compute(int x, int y);
4
5 }
定义一个ComputerImpl类,该类中有一个类方法add()。

1 class ComputerImpl {
2
3 static int add(int x, int y) {
4 return x + y;
5 }
6
7 }
类方法的方法引用。

1 @Test
2 void testMethodReferences() {
3 Computer add = ComputerImpl :: add;
4 }
实例方法的方法引用
实例方法的方法引用有两种格式:
通过实例引用实例方法
通过实例引用实例方法的格式为:引用类的类实例 :: 引用类的实例方法名。该方式需要抽象方法的参数与引用的实例方法的参数一致,返回值类型也要一致。
定义一个Computer函数式接口,该接口中有一个抽象方法compute()。

1 interface Computer {
2
3 float compute(int x, int y);
4
5 }
定义一个ComputerImpl类,该类中有一个实例变量f和一个实例方法divide()。

1 class ComputerImpl {
2
3 float f;
4
5 float divide(int x, int y) {
6 return x / y * f;
7 }
8
9 }
通过实例引用实例方法的方法引用。

1 @Test
2 void testMethodReferences() {
3 ComputerImpl computerImpl = new ComputerImpl();
4 computerImpl.f = 1.0f;
5 Computer divide = computerImpl :: divide;
6 }
通过类名引用实例方法
通过类名引用实例方法的格式为:引用类的类名 :: 引用类的实例方法名。该方式需要抽象方法的第一个参数为引用类实例,之后的参数与引用的实例方法的参数一致,返回值类型也要一致。注意引用类实例参数必须是抽象方法的第一个参数,不能放在其他位置。
定义一个Computer函数式接口,该接口中有一个抽象方法compute()。

1 interface Computer {
2
3 float compute(ComputerImpl computer, int x, int y);
4
5 }
定义一个ComputerImpl类,该类中有一个实例变量f和一个实例方法divide()。

1 class ComputerImpl {
2
3 float f;
4
5 float divide(int x, int y) {
6 return x / y * f;
7 }
8
9 }
通过类名引用实例方法的方法引用。

1 @Test
2 void testMethodReferences() {
3 Computer divide = ComputerImpl :: divide;
4 }
两种方式的区别
通过实例引用实例方法是先创建一个实例,然后通过实例引用实例方法。当实例改变时,需要再次引用实例方法。
通过类名引用实例方法是直接通过类名引用实例方法。这种方式引用实例方法需要抽象方法的第一个参数为引用类的实例。当实例改变时,只需在调用方法时传入新的实例即可,无需再次引用实例方法。
构造方法的方法引用
构造方法的方法引用的格式为:引用类的类名 :: new。该方式需要抽象方法的参数与构造方法的参数一致,返回值类型为引用类的类型。
定义一个Computer函数式接口,该接口中有一个抽象方法newInstance()。

1 interface Computer {
2
3 ComputerImpl newInstance(int x, int y);
4
5 }
定义一个ComputerImpl类,该类中有两个实例变量x和y,以及一个带参构造方法。

1 class ComputerImpl {
2
3 int x;
4 int y;
5
6 public ComputerImpl(int x, int y) {
7 this.x = x;
8 this.y = y;
9 }
10
11 }
构造方法的方法引用。

1 @Test
2 void testMethodReferences() {
3 Computer computer = ComputerImpl :: new;
4 }
来源:https://www.cnblogs.com/lqkStudy/p/11031940.html
