🌷一、Lambda表达式
🌸1.1 什么是lambda表达式
Lambda
表达式是Java8
引入的一个重要功能,是一个匿名函数,我们可以把Lambda
表达式理解为是一段可以传递的匿名函数(将代码像数据一样进行传递)。
🌹1.2 为什么要有lambda表达式
假设我们有一个支付方式接口,该接口有微信和支付宝两种实现方式,我们有几种方式来实现,第一种就是接口实现方式,第二种就是内部类,第三种就是用匿名函数来实现。
1.2.1 接口
我们在调用支付方式时,直接new
一个对应的支付方式实现类的对象接口。
- 支付方式接口
java
public interface PaymentMethod {
/**
* 支付
* @param price
*/
void payment(int price);
}
- 支付方式两种接口:微信支付和支付宝支付
java
// 支付宝支付方式实现
public class AliPaymentMethod implements PaymentMethod {
@Override
public void payment(int price) {
System.out.println("支付宝支付了:" + price + "元");
}
}
// 微信支付方式实现
public class WeChatPaymentMethod implements PaymentMethod {
@Override
public void payment(int price) {
System.out.println("微信支付了:" + price + "元");
}
}
- 在订单中支付时,选择支付方式
java
@Test
public void test() {
// 如果是微信支付,就new一个微信支付对象
PaymentMethod paymentMethod1 = new WeChatPaymentMethod();
paymentMethod1.payment(10);
// 如果是支付宝支付,就new一个支付宝支付对象
PaymentMethod paymentMethod2 = new AliPaymentMethod();
paymentMethod2.payment(10);
}
1.2.2 内部类
用接口实现类的方式,可以直接new
需要的支付方式,但是类会变得很多,一种支付方式,就需要一种实现类。用内部类的方式就可以避免这个问题
java
@Test
public void test2() {
PaymentMethod paymentMethod = new PaymentMethod() {
@Override
public void payment(int price) {
System.out.println("支付了" + price + "元");
}
};
paymentMethod.payment(10);
}
🌺1.3 Lambda表达式
1.3.1 lambda表达式固定格式
lambda
表达式由三部分组成:形式参数、箭头符号、代码块。
🥇形式参数(左侧):lambda
表达式的参数列表,用()括起来的。
🥈箭头符号(中间):->
🥉代码块(右侧):lambda
表达式的代码,用{}括起来的。
1.3.2 lambda的几种格式
- 格式一:无参数、无返回值:()-> {}
java
public interface MyInterface {
void inter();
}
@Test
public void test1() {
MyInterface myInterface = ()->{
System.out.println("myInterface");
};
myInterface.inter();
}
- 格式二:有参数,无返回值:(paraName)->{}
java
public interface MyInterface {
void inter(String account);
}
@Test
public void test1() {
MyInterface myInterface = (x)->{
System.out.println("param: " + x);
};
myInterface.inter("xyz");
}
- 格式三:有参数,有返回值:(paraName)->{}
java
public interface MyInterface {
String inter(String account);
}
@Test
public void test1() {
MyInterface myInterface = (x)->{
return "参数是:" + x;
};
myInterface.inter("xyz");
}
1.3.3 lambda省略规则
🥇lambda
表达式的参数列表的数据类型可以省略不写
🥈当且只有一个参数时,小括号可以省略
🥉如果代码块只有一行代码时,大括号{}和return
关键字都可以省略。
🌻1.4 lambda实现
1.4.1 lambda实现
前面我们的支付接口,有接口实现类和内部类两种方式,都有一定的缺点,现在我们用lambda
表达式来实现。
用lambda
表达式一行就可以解决问题,这样代码简洁了很多。
java
public interface PaymentMethod {
/**
* 支付
* @param price
*/
void payment(int price);
}
@Test
public void test3() {
PaymentMethod paymentMethod = (price) -> System.out.println("支付了" + price + "元");
paymentMethod.payment(10);
}
这里我们再写一个例子,接口作为函数参数,lambda
作表达式为数据传递。
java
public interface PaymentMethod {
/**
* 支付
* @param price
*/
void payment(int price);
}
@Test
public void test1() {
payOrder((price)-> System.out.println("已支付订单:" + price + "元"));
}
public void payOrder(PaymentMethod paymentMethod) {
paymentMethod.payment(10);
}
1.4.2 lambda和内部类区别
匿名内部类可以是接口,也可以是抽象类,还可以是具体类,并且接口中可以有多个抽象方法。
lambda表达式只能是接口,并且接口中只能有一个抽象方法。
🌼1.5 Lambda作用域
lambda
表达式的作用域就是lambda
表达式能访问哪些作用域的变量。也就是lambda
表达式能捕获哪些变量。
lambda
表达式能捕获三种作用域的变量:实例变量、静态变量、局部变量。
对于实例变量和静态变量,lambda表达式都有读写权限。对于局部变量只有读取final
和effectively final
类型的变量。
final类型就是变量初始化之后就不会改变了
effectively final类型变量其实就是虽然不是final修饰的,但是初始化之后没有手动去重新赋值,一直没有变动。
- 实例变量对于实例变量,在
lambda
表达式中可以进行读写操作。
java
public interface PaymentMethod {
/**
* 支付
* @param price
*/
void payment(int price);
}
@Test
public void test1() {
payOrder((price)-> System.out.println("已支付订单:" + price + "元"));
}
public void payOrder(PaymentMethod paymentMethod) {
paymentMethod.payment(10);
}
- 静态变量 对于静态变量,在
lambda
表达式中可以进行读写操作。
java
public interface PaymentMethod {
/**
* 支付
* @param price
*/
void payment(int price);
}
private static int y = 2;
@Test
public void test1() {
PaymentMethod paymentMethod = (price) -> {
y = y + 1;
System.out.println("y:" + y + ", price:" + price);
};
paymentMethod.payment(10);
}
- 局部变量 对于局部变量,
lambda
表达式只有读取权限。
在lambda
表达式中对局部变量进行了修改,会直接报错如下,意思是变量用在lambda
表达式中应该是final
或者effectively final
类型的,也就是不能改变,要改变可以把局部变量改成atomic
类型的。
Variable used in lambda expression should be final or effectively final
java
//
public interface PaymentMethod {
/**
* 支付
* @param price
*/
void payment(int price);
}
@Test
public void test1() {
int x = 1;
PaymentMethod paymentMethod = (price) -> {
x = x + 1;
};
paymentMethod.payment(10);
}
为什么局部变量不能被修改呢,因为局部变量是存储在方法栈中的,而lambda
表达式可能在另外一个线程中执行,这样方法栈的变量就会被多个线程修改,出现线程安全问题。如下面这个例子,就是lambda
表达式在另外一个线程中执行。这里对局部变量x
只能读取。
java
@Test
public void test2() {
int x = 1;
Runnable runnable = () -> {
System.out.println("x is " + x);
};
Thread thread = new Thread(runnable);
thread.start();
}
🏵️1.6 lambda的this
lambda
表达式的this
是创建lambda
表达式方法的this
。比如这两个lambda
表达式,第二个虽然新开了一个线程,但是输出的this
还是执行当前方法所在的this
。
java
@Test
public void test1() {
PaymentMethod paymentMethod = (price) -> {
System.out.println( this);
};
paymentMethod.payment(10);
}
@Test
public void test2() {
int x = 1;
Runnable runnable = () -> {
System.out.println(this);
};
Thread thread = new Thread(runnable);
thread.start();
}