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();
}
相关推荐
追逐时光者10 分钟前
在 Blazor 中使用 Chart.js 快速创建数据可视化图表
后端·.net
橘猫云计算机设计17 分钟前
基于ssm的食物营养成分数据分析平台设计与实现(源码+lw+部署文档+讲解),源码可白嫖!
后端·python·信息可视化·数据挖掘·数据分析·django·毕业设计
Source.Liu18 分钟前
【学Rust写CAD】24 扫描渐变(sweep_gradient.rs)
后端·rust
Asthenia041224 分钟前
MP:从Wrapper到源码分析
后端
Foyo Designer30 分钟前
【 <二> 丹方改良:Spring 时代的 JavaWeb】之 Spring Boot 中的国际化:支持多语言的 RESTful API
java·spring boot·redis·后端·spring·缓存·restful
嘉友1 小时前
Redis zset数据结构以及时间复杂度总结(源码)
数据结构·数据库·redis·后端
苏三说技术2 小时前
Excel百万数据如何快速导入?
后端
昵称为空C2 小时前
SpringBoot编码技巧-ScheduledExecutorService轮询
java·spring boot·后端
huangyingying20253 小时前
03-分支结构
后端
00后程序员3 小时前
【Flutter -- 基础组件】Flutter 导航栏
后端