02-JDK新特性-Lambda表达式

JDK新特性

Lambda表达式

什么是Lambda表达式

Lambda表达式是一个匿名代码块,用于简单的传递一段代码片段。

Lambda表达式标准格式

格式:(形式参数) -> {代码块}

  • 形式参数

    如果有多个参数,参数只见用逗号隔开;如果没有,留空即可
  • ->

    由英文中画线和大于符号组成,固定写法。代表指向动作。
  • 代码块

    是我们具体要做的事情,也就是我们以前写的方法体内容
// 组成Lambda表达式的三要素:形式参数,箭头,代码块
// 示例 
new Thread(() -> System.out.println("这是一个示例")).start();

Lambda表达式的使用前提

  • 有一个接口
  • 接口中有且仅有一个抽象方法

理解Lambda表达式

定义一只接口(Eatable),里面定义一个抽象方法:void eat();表示吃这一个动作。
然后以实现类,匿名内部类,Lambda表达式分别实现一下小明吃饭了这一动作。

通用接口 Eatable.java
package demo01;

/**
 * 定义吃接口
 *
 * @author Anna.
 * @date 2024/3/31 22:35
 */
public interface Eatable {
    /**
     * 吃
     *
     * @param
     * @return void
     * @author Anna.
     * @date 2024/3/31 22:35
     */
    void eat();
}
实现类方式实现

创建一个实现类(EatImpl)实现(Eatable)接口

package demo01;

/**
 * 吃实现类
 *
 * @author Anna.
 * @date 2024/3/31 22:37
 */
public class EatImpl implements Eatable{
    @Override
    public void eat() {
        System.out.println("通过实现类-小明吃东西了");
    }
}

测试代码

package demo01;

import java.util.Arrays;

/**
 * Demo01:
 * 定义一只接口(Eatable),里面定义一个抽象方法:void eat();表示吃这一个动作。
 * 然后以实现类,匿名内部类,Lambda表达式分别实现一下小明吃饭了这一动作。
 *
 * @author Anna.
 * @date 2024/3/31 22:36
 */
public class LambdaDemo01 {
    public static void main(String[] args) {
        // 通过实现类实现
        Eatable eat = new EatImpl();
        eat.eat();
    }
}

执行结果

在这里插入图片描述

匿名内部类方式实现

测试代码

package demo01;

import java.util.Arrays;

/**
 * Demo01:
 * 定义一只接口(Eatable),里面定义一个抽象方法:void eat();表示吃这一个动作。
 * 然后以实现类,匿名内部类,Lambda表达式分别实现一下小明吃饭了这一动作。
 *
 * @author Anna.
 * @date 2024/3/31 22:36
 */
public class LambdaDemo01 {
    public static void main(String[] args) {
        // 匿名内部类方式实现
        eat(new Eatable() {
            @Override
            public void eat() {
                System.out.println("通过匿名内部类方式实现-小明吃东西了");
            }
        });
    }

    /**
     * 定义一个调用Eatable接口eat()的方法
     *
     * @param eatable
     * @return void
     * @author Anna.
     * @date 2024/3/31 22:50
     */
    private static void eat(Eatable eatable){
        eatable.eat();
    }
}

执行结果

在这里插入图片描述

Lambda表达式实现

测试代码

package demo01;

import java.util.Arrays;

/**
 * Demo01:
 * 定义一只接口(Eatable),里面定义一个抽象方法:void eat();表示吃这一个动作。
 * 然后以实现类,匿名内部类,Lambda表达式分别实现一下小明吃饭了这一动作。
 *
 * @author Anna.
 * @date 2024/3/31 22:36
 */
public class LambdaDemo01 {
    public static void main(String[] args) {
        // Lambda表达式实现
        eat(() -> {
            System.out.println("通过Lambda表达式实现-小明吃东西了");
        });
    }

    /**
     * 定义一个调用Eatable接口eat()的方法
     *
     * @param eatable
     * @return void
     * @author Anna.
     * @date 2024/3/31 22:50
     */
    private static void eat(Eatable eatable) {
        eatable.eat();
    }
}

执行结果

在这里插入图片描述

对比实现类,匿名内部类,Lambda表达式编译文件

在这里插入图片描述

通过实现类方式实现,会生成EatImpl.class文件

通过匿名内部类方式实现,会生成LambdaDemo01$1.class匿名内部类文件

通过Lambda方式实现,不会额外生成文件,实现字节码会动态生成

Lambda表达式的省略模式

省略规则:

  1. 参数类型可以省略,但是多个参数的情况下,不能只省略一个
  2. 如果参数有且仅有一个,那么小括号可以省略
  3. 如果代码块的语句只有一条,大括号和分号可以省略,甚至是return

测试代码

package demo02;

import demo01.Eatable;

/**
 * Demo02:
 * Lambda表达式省略模式
 *
 * @author Anna.
 * @date 2024/3/31 22:36
 */
public class LambdaDemo02 {
    public static void main(String[] args) {
        // 正常调用
        System.out.println("=========正常调用=========");
        eat(() -> {
            System.out.println("小明吃东西了");
        });
        sum((int x, int y) -> {
            return x + y;
        });
        sleep((String name) -> {
            System.out.println(name + "睡觉了");
        });

        // 省略模式
        System.out.println("=========省略模式=========");
        System.out.println("=========参数类型可以省略=========");
        sleep((name) -> {
            System.out.println(name + "睡觉了");
        });
        // 参数类型可以省略,但是多个参数的情况下,不能只省略一个
        System.out.println("=========参数类型可以省略,但是多个参数的情况下,不能只省略一个=========");
        sum((x, y) -> {
            return x + y;
        });

        // 如果参数有且仅有一个,那么小括号可以省略
        System.out.println("=========如果参数有且仅有一个,那么小括号可以省略=========");
        sleep(name -> {
            System.out.println(name + "睡觉了");
        });

        // 如果代码块的语句只有一条,大括号和分号可以省略
        System.out.println("=========如果代码块的语句只有一条,大括号和分号可以省略=========");
        sleep(name -> System.out.println(name + "睡觉了"));

        // 如果代码块的语句只有一条,且有返回值 return 也可以省略
        System.out.println("=========如果代码块的语句只有一条,且有返回值 return 也可以省略=========");
        sum((x, y) -> x + y);
    }

    /**
     * 定义一个调用Eatable接口eat()的方法
     *
     * @param eatable
     * @return void
     * @author Anna.
     * @date 2024/3/31 23:11
     */
    private static void eat(Eatable eatable) {
        eatable.eat();
    }

    /**
     * 定义一个调用Sumable接口sum()的方法
     *
     * @param sumable
     * @return void
     * @author Anna.
     * @date 2024/3/31 23:11
     */
    private static void sum(Sumable sumable) {
        System.out.println(sumable.sum(20, 30));
    }

    /**
     * 定义一个调用Sleepable接口sleep()的方法
     *
     * @param sleepable
     * @return void
     * @author Anna.
     * @date 2024/3/31 23:11
     */
    private static void sleep(Sleepable sleepable) {
        sleepable.sleep("小明");
    }
}

执行结果

在这里插入图片描述

Lambda作用域

在Lambda表达式中访问外层作用域和旧版本的匿名对象中的方式类似。你可以直接访问标记了final的外层局部变量,或者实例的字段以及静态变量。

Lambda表达式不会从超类(supertype)中继承任何变量名,也不会引入一个新的作用域。

Lambda表达式基于词法作用域,也就是说lambda表达式函数体里面的变量和它外部环境的变量具有相同的语义(也包括lambda表达式的形式参数)。

此外,this关键字及其引用,在Lambda表达式内部和外部也拥有相同的语义。

访问局部变量
可以直接在lambda表达式中访问外层的局部变量<br/>
在Lambda 表达式当中被引用的变量的值不可以被更改。<br/>
在 Lambda 表达式当中被引用的变量的值不可以被更改。

案例

public class LambdaDemo5 {

    public static void main(String[] args) {
        final String count = "123";
        int num = 0;
        new Thread(() -> {
            num = 100;  // 编译报错
            count = "232";    // 编译报错
        }, "Lambda表达式").start();
    }
}

编译结果

在这里插入图片描述

访问外层的局部变量

可以直接在lambda表达式中访问外层的局部变量。

lambda表达式的局部变量(eg:num)可以不用声明为final,不过这里的局部变量(eg:num)必须不可被后面的代码修改(即隐性的具有final的语义)。

案例

public class LambdaDemo501 {

    public static void main(String[] args) {
        final String count = "123";
        int num = 0;
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "num:" + num);
            System.out.println(Thread.currentThread().getName() + "count:" + count);
        }, "Lambda表达式").start();
    }
}

执行结果

在这里插入图片描述

Lambda表达式中的this
Lambda 表达式中使用 this 会引用创建该 Lambda 表达式的方法的 this 参数。

案例

package demo05;


import java.util.function.Supplier;

/**
 * Lambda作用域
 *
 * @author Anna.
 * @date 2024/4/1 11:07
 */
public class LambdaDemo05 {

    public static void main(String[] args) {
        LambdaDemo05 lambdaDemo05 = new LambdaDemo05();
        lambdaDemo05.scopeTest01();
    }

    public void scopeTest01() {
        System.out.println("方法体中使用this:" + this);
        pase(() -> {
            System.out.println("Lambda中使用this:" + this);
            return null;
        });
    }

    public static <T> T pase(Supplier<T> supplier) {
        return supplier.get();
    }
}

执行结果

在这里插入图片描述

方法引用

什么是方法引用

::该符号为引用运算符,而它所在的表达式被称为方法引用

方法引用的使用理解

通过实现将字符串数字转换为int类型,来体验方法引用

Conversion.java
package demo04;

/**
 * 定义一个接口将字符串数字转换成int类型
 *
 * @author Anna.
 * @date 2024/4/1 11:06
 */
public interface Conversion {
    int pase(String s);
}
LambdaDemo04.java
package demo04;

/**
 * 方法引用
 *
 * @author Anna.
 * @date 2024/4/1 11:07
 */
public class LambdaDemo04 {

    public static void main(String[] args) {
        // 使用Lambda表达式实现
        System.out.println("使用Lambda表达式实现");
        pase(s -> Integer.parseInt(s));

        // 使用方法引用实现
        System.out.println("使用方法引用实现");
        pase(Integer::parseInt);
    }

    public static void pase(Conversion c) {
        System.out.println(c.pase("123"));
    }
}

执行结果

在这里插入图片描述

回顾分析:

Lambda表达式: pase(s -> Integer.parseInt(s));

拿到参数s之后,传递给Integer.parseInt方法处理,返回结果

方法引用:pase(Integer::parseInt);

直接使用Integer::parseInt方法来取代Lambda表达式,代码更加简洁

结论:

如果使用Lambda表达式,那么根据“可推导即可省略”的原则,无需指定参数类,也无需指定的重载形式,他们都将被自动推导

如果使用方法引用,也同样可以根据上下文进行推导

方法引用类型

方法引用可以被分为一下几类:

  1. 静态方法引用:引用静态的方法

格式: 类名::静态方法

  1. 实例方法引用:

格式: 对象::成员方法

示例: pase(“HelloWorld”::toUpperCase); 等价于 pase(s -> s.toUpperCase());

  1. 引用类中的非静态方法:

格式: 类名::成员方法

  1. 引用类的构造方法:

格式: 类名::new

Sumable.java

package demo3;

public interface Sumable {
    int sum(int x, int y);
}

SumImpl.java

package demo3;

public class SumImpl implements Sumable {

    @Override
    public int sum(int x, int y) {
        return x + y;
    }
}

SumUtils.java

package demo3;

public class SumUtils {
    public int sum(int x, int y) {
        return x + y;
    }
}

IEntityDo.java

package demo3;

public interface IEntityDo {
    SumEntity init(int x, int y);
}

SumEntity.java

package demo3;

public class SumEntity {
    private int x;
    private int y;

    public SumEntity(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public String toString() {
        return "SumEntity{" +
                "x=" + x +
                ", y=" + y +
                '}';
    }
}

LambdaDemo03.java

package demo3;

/**
 * Demo03:
 * Lambda表达式方法引用
 *
 * @author Anna.
 * @date 2024/3/31 22:36
 */
public class LambdaDemo03 {
    public static void main(String[] args) {
        System.out.println("Lambda方法");
        sum(((x, y) -> x + y));

        System.out.println("静态方法引用");
        sum(Integer::sum);

        System.out.println("实例方法引用");
        Sumable sumable = new SumImpl();
        sum(sumable::sum);
        System.out.println("如果该接口有且仅有一个抽象方法,还可以进一步省略::内容");
        sum(sumable);

        System.out.println("引用类中的非静态方法");
        SumUtils sumUtils = new SumUtils();
        sum(sumUtils::sum);

        System.out.println("引用类的构造方法");
        init(SumEntity::new);
    }

    /**
     * 定义一个调用Sumable接口sum()的方法
     *
     * @param sumable
     * @return void
     * @author Anna.
     * @date 2024/3/31 23:11
     */
    private static void sum(Sumable sumable) {
        System.out.println(sumable.sum(20, 30));
    }

    /**
     * 定义一个调用EntityDo接口init()的方法
     *
     * @param entityDo
     * @return void
     * @author Anna.
     * @date 2024/3/31 23:11
     */
    private static void init(IEntityDo entityDo) {
        System.out.println(entityDo.init(20, 30));
    }
}

执行结果

在这里插入图片描述

Lambda表达式优缺点

Java 8中引入了lambda表达式,使得代码编写更加简洁、优雅。lambda表达式是一个匿名函数,可以作为参数传递给方法或者存储在变量中。在Java中使用lambda表达式可以有以下优点和缺点:

优点

  1. 代码简洁:使用lambda表达式可以减少冗余的代码量,使得代码更加简洁明了。特别是在编写只有一个抽象方法的接口时,可以将其替换为lambda表达式。
  2. 促进函数式编程:lambda表达式使得函数式编程成为可能。函数式编程强调函数的纯粹性和不可变性,有助于减少副作用和提高代码的可维护性。通过lambda表达式,我们可以将函数作为一等公民,并在不改变状态的情况下进行操作。
  3. 更好的代码复用:使用lambda表达式可以更方便地实现代码的复用。可以将一段逻辑封装在一个lambda表达式中,然后在多个地方使用。
  4. 并行处理能力:在Java 8之前,要实现多线程或并行处理需要编写大量的代码。使用lambda表达式可以方便地编写并行处理的代码,充分利用多核处理器的性能。
  5. 提升代码可读性:lambda表达式将逻辑封装在一个更加紧凑的形式中,使得代码更易读。通过命名良好的lambda表达式,可以使代码的意图更加清晰。

缺点

  1. 学习成本:lambda表达式是一个新的概念,在Java中引入了新的语法。对于初学者来说,学习和理解lambda表达式需要一些时间和努力。
  2. 可读性差:虽然lambda表达式可以提升代码的可读性,但是过于复杂的lambda表达式可能反而降低代码的可读性。尤其是当lambda表达式包含多个参数、多行代码或复杂的逻辑时,可读性会受到影响。
  3. 调试困难:由于lambda表达式是匿名函数,调试时可能会更加困难。在调试过程中,无法直接跟踪lambda表达式的执行路径,可能会增加调试的难度。
  4. 性能问题:lambda表达式的性能可能比传统的方法调用略差。虽然这个差距很小,但是在某些高性能场景下可能会有所影响。

总结:

lambda表达式是Java 8引入的一项强大的功能,对于简化代码、促进函数式编程、提升代码可读性和代码复用等方面都有很大的帮助。

然而,它也存在着一些学习成本高、可读性差、调试困难和性能问题等缺点。

在使用lambda表达式时,需要根据具体情况权衡利弊,选择合适的使用方式。

gitee源码

git clone https://gitee.com/dchh/JavaStudyWorkSpaces.git

  • 11
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丨Anna丨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值