Java 基础 —— Lambda 表达式

Lambda 表达式是 Java 8 的重要更新,它支持将代码块作为方法参数、允许使用更简洁的代码来创建只有一个抽象方法的接口的实例

描述中提到的接口称为函数式接口

语法

Lambda 表达式的主要作用就是可以用于简化创建匿名内部类对象,Lambda 表达式的代码块将会用于实现抽象方法的方法体,Lambda 表达式就相当于一个匿名方法。

Lambda 表达式由三部分组成:

->

示例

interface Eatable {
    void taste();
}

interface Flyable {
    void fly(String weather);
}

interface Addable {
    int add(int a, int b);
}


public class LambdaQs {
    // 调用该方法需要传入一个 Eatable 类型的对象
    public void eat(Eatable e) {
        System.out.println(e);
        e.taste();
    }

    // 调用该方法需要传入 Flyable 类型的对象
    public void drive(Flyable f) {
        System.out.println("我正在驾驶:" + f);
        f.fly("「夏日晴天」");
    }

    // 调用该方法需要 Addable 类型的对象
    public void calc(Addable add) {
        System.out.println("5 + 3 = " + add.add(5, 3));
    }

    public static void main(String[] args) {
        LambdaQs lq = new LambdaQs();
        // Lambda 表达式的代码块只有一句,因此省略了花括号
        lq.eat(() -> System.out.println("雪糕的味道不错!"));
        // Lambda 表达式的形参只有一个参数,因此省略了圆括号
        lq.drive(weather -> {
            // 对接口中抽象方法 fly 的重写
            System.out.println("今天天气是:" + weather);
            System.out.println("飞机平稳飞行!");
        });
        // Lambda 表达式只有一条语句,即使该表达式需要返回值,也可以省略 return
        lq.calc((a, b) -> a + b);
        // 如果不用 Lambda 表达式,就需要如下匿名类的方式去重写抽象方法
        lq.calc(new Addable() {
            @Override
            public int add(int a, int b) {
                return a + b;
            }
        });
    }
}

输出结果:

oop.lambda.LambdaQs$$Lambda$1/1607521710@7ef20235
雪糕的味道不错!
我正在驾驶:oop.lambda.LambdaQs$$Lambda$2/1329552164@15aeb7ab
今天天气是:「夏日晴天」
飞机平稳飞行!
5 + 3 = 8
5 + 3 = 8

以上示例可以说明,Lambda 表达式实际上可以被当做一个具体的对象。

Lambda 表达式与函数式接口

Lambda 表达式的类型,也被称为「目标类型( target type
)」。
Lambda 表达式的目标类型必须是「函数式接口( functional interface
)」

。函数式接口代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法、类方法,但仅能声明一个抽象方法。

查询 Java 8 的 API 文档,可以发现大量的函数式接口,例如:Runnable、ActionListener 等接口都是函数式接口。

Java 8 专门为函数式接口提供了 @FunctionalInterface
注解。该注解就是用于告诉编译器校验接口必须是函数式接口,否则就报错。

由于 Lambda 表达式的结果就是被当做对象/实例,因此,可以使用 Lambda 表达式进行赋值,示例:

Runnable r = () -> {
    for (int i = 0; i < 100; i++) {
        System.out.println(i);
    }
};

我们看一下 Runnable 接口的定义:

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

看一个错误示例:

Object obj = () -> {
    for (int i = 0; i < 100; i++) {
        System.out.println(i);
    }
};

上面这段代码会报错: Target type of a lambda conversion must be an interface
。Lambda 表达式的目标类型必须是明确的函数式接口!将 Lambda 表达式赋值给 Object 类型的变量,编译器只能推断出它的表达类型为 Object,而 Object 并不是函数式接口,因此就报错了!

为了保证 Lambda 表达式的目标类型是明确的函数式接口,有如下三种常见方式:

  • 将 Lambda 表达式赋值给函数式接口类型的变量;
  • 将 Lambda 表达式作为函数式接口类型的参数传给某个方法;
  • 使用函数式接口对 Lambda 表达式进行强制类型转换;

将上面出错的代码可以进行如下的改写:

Object obj1 = (Runnable)() -> {
    for (int i = 0; i < 100; i++) {
        System.out.println(i);
    }
};

综上,Lambda 表达式的本质很简单,就是使用简单的语法来创建函数式接口的实例,避免匿名内部类的繁琐。

方法引用于构造器引用

如果 Lambda 表达式的代码块只有一条代码,还可以在代码中使用方法引用和构造器引用。

方法引用和构造器引用的好处是使 Lambda 表达式的代码块更加简洁。方法引用和构造器引用都需要使用两个英文冒号 ::

种类 示例 说明 对应的 Lambda 表达式
引用类方法 类名::类方法 函数式接口中被实现的方法的全部参数传给该类方法作为参数 (a,b,...) -> 类名.类方法(a,b,...)
引用特定对象的实例方法 特定对象::实例方法 函数式接口中被实现的方法的全部参数传给该方法作为参数 (a,b,...) -> 特定对象.实例方法(a,b,...)
引用某类对象的实例方法 类名::实例方法 函数式接口中被实现的方法的第一个参数作为调用者,后面的参数全部传给该方法作为参数 (a,b,...)->a.实例方法(b,...)
引用构造器 类名::new 函数式接口中被实现方法的全部参数传给该构造器作为参数 (a,b,...)->new 类名(a,b,...)

原文 

https://michael728.github.io/2020/07/04/java-basic-lambda/

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Java 基础 —— Lambda 表达式

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址