Java8(一):lambda表达式

🌷一、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表达式都有读写权限。对于局部变量只有读取finaleffectively 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();
}
相关推荐
编程乐学(Arfan开发工程师)4 小时前
56、原生组件注入-原生注解与Spring方式注入
java·前端·后端·spring·tensorflow·bug·lua
Elcker6 小时前
Springboot+idea热更新
spring boot·后端·intellij-idea
南玖yy8 小时前
深入理解 x86 汇编中的符号扩展指令:从 CBW 到 CDQ 的全解析
开发语言·汇编·arm开发·后端·架构·策略模式
江梦寻9 小时前
软件工程教学评价
开发语言·后端·macos·架构·github·软件工程
美好的事情能不能发生在我身上9 小时前
苍穹外卖Day11代码解析以及深入思考
java·spring boot·后端·spring·架构
不良手残10 小时前
Redisson + Lettuce 在 Spring Boot 中的最佳实践方案
java·spring boot·redis·后端
一线大码11 小时前
SpringBoot 和 MySQL 的事务隔离级别关系
spring boot·后端·mysql
罗政11 小时前
基于 SpringBoot + Vue 在线点餐系统(前后端分离)
vue.js·spring boot·后端
曼岛_11 小时前
[架构之美]深入优化Spring Boot WebFlux应用
spring boot·后端·架构