前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 8 新特性|Lambda表达式

Java 8 新特性|Lambda表达式

作者头像
Java小技巧
发布2022-05-23 12:48:47
2600
发布2022-05-23 12:48:47
举报
文章被收录于专栏:Java小技巧

从Java8出现以来lambda是最重要的特性之一,它可以让我们用简洁流畅的代码完成一个功能。Lambda 表达式是函数式编程的的一个重要特性,标志着 Java 向函数式编程迈出了重要的第一步。

Lambda 表达式初体验

Java 8之前写代码:

代码语言:javascript
复制
Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("你好、二哥!");
    }
}

用Lambda表达式之后:

代码语言:javascript
复制
Runnable r =() -> System.out.println("你好,二哥!我是Lambda表达式。");

没有对比、没有伤害、是不是很优秀?

Lambda 表达式语法

语法结构如下:

代码语言:javascript
复制
parameter -> expression body

Java8中引入了一个新的操作符 "->" 该操作符称为箭头操作符或 Lambda 操作符

箭头操作符将 Lambda 表达式拆分成两部分:

左侧:Lambda 表达式的参数列表 右侧:Lambda 表达式中所需执行的功能, 即 Lambda 体

伪代码:

代码语言:javascript
复制
//有参数且只有一条语句时
int sum = (x,y) -> x + y
//只有一个参数时
x -> x
//没有参数时
() -> System.out.println("你好,二哥!我是Lambad表达式。")
//有多条语句时
(x,y) -> {
    int z = x + y;
    System.out.println("你好,二哥!我是Lambad表达式。")
}
代码语言:javascript
复制
注:
  • 可选的参数类型声明 :无需声明参数的类型。编译器可以从参数的值推断出相同的值。
  • 可选的参数周围的小括号 () :如果只有一个参数,可以忽略参数周围的小括号。但如果有多个参数,则必须添加小括号。
  • 可选的大括号 {} : 如果 Lambda 表达式只包含一条语句,那么可以省略大括号。但如果有多条语句,则必须添加大括号。
  • 可选的 return 关键字 :如果 Lambda 表达式只有一条语句,那么编译器会自动 return 该语句最后的结果。但如果显式使用了 return 语句,则必须添加大括号 {} ,哪怕只有一条语句。

Lambda 表达式的原理

Lambda 表达式其实是一个特殊的只有一个方法的类的实例。

这些类是 Java 8 内部已经定义好的,而且实现了 java.lang.FunctionalInterface 这个接口。

这个 java.lang.FunctionalInterface 接口是一种信息性注解类型,用于标识一个接口类型声明为函数接口( functional interface )。

从某些方面说,Java 8 的 Lambda 表达式是使用匿名内部类的语法创建了 java.util.function 包下相应签名的接口的或者其它自定义的只有一个方法的接口实例。

实际上,Java 8 中的 Lambda 不仅仅是使用匿名内部类,还使用了 Java 8 接口的默认方法和一些其它的功能。

代码范例:

代码语言:javascript
复制
package com.sjh.test.java8;

public class TestLambda {

    public static void main(String args[])
    {
        TestLambda tester = new TestLambda();

        // 有声明参数类型
        MathOperation addition = (int a, int b) -> a + b;

        // 没有声明参数类型
        MathOperation subtraction = (a, b) -> a - b;

        // 使用 return 语句显式返回值需要添加大括号
        MathOperation multiplication = (int a, int b) -> { return a * b; };

        // 如果只有一条语句,那么可以省略大括号,Java 会返回表达式的值
        MathOperation division = (int a, int b) -> a / b;

        System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
        System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
        System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
        System.out.println("10 / 5 = " + tester.operate(10, 5, division));
    }

    interface MathOperation {
        int operation(int a, int b);
    }

    private int operate(int a, int b, MathOperation mathOperation) {
        return mathOperation.operation(a, b);
    }
}

运行结果:

代码语言:javascript
复制
/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/... com.sjh.test.java8.TestLambda
10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2

Process finished with exit code 0

总结:为那些函数接口定义了它们包含的唯一方法,而且返回函数接口的实例

Lambda 表达式的缺点

Java Lambda 表达式最大的缺点,就是不能像其它语言的 Lambda 表达式一样凭空出现。

Java 中的 Lambda 表达式需要有一个函数接口声明作为模板。这个模板定义了 Lambda 表达式的参数类型和返回值类型。

例如下面的代码,我们先要声明一个函数接口类型,然后才能定义一个参数和返回值都一样的表达式

代码范例:

代码语言:javascript
复制
package com.sjh.test.java8;

public class TestLambdaFirst {

    // 先声明一个函数接口
    interface GreetingService {
        void sayMessage(String message);
    }

    public static void main(String args[])
    {
        TestLambdaFirst tester = new TestLambdaFirst();

        // 有小括号
        GreetingService greetService1 = message ->
                System.out.println("你好," + message);

        // 省略小括号
        GreetingService greetService2 = (message) ->
                System.out.println("你好," + message);

        greetService1.sayMessage("二哥!");
        greetService2.sayMessage("我是Lambda。");
    }
}

运行结果:

代码语言:javascript
复制
/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/...com.sjh.test.java8.TestLambdaFirst
你好,二哥!
你好,我是Lambda。

Process finished with exit code 0

Lambda 表达式作用域(scope)

因为 Java 8 的 Lambda 表达式其实是函数接口的内联实现,也就是匿名内部类,因此,可以引用任何外部的变量或者常量。

但是,Lambda 对这些外部的变量是有要求的:它们必须使用 final 修饰符修饰。

如果一个变量允许被第二次赋值,则 Lambda 表达式会抛出编译错误。

1、表达式使用外部 final 变量:

代码语言:javascript
复制
package com.sjh.test.java8;

public class TestLambdaSecond {

    static String salutation = "你好,";

    public static void main(String args[])
    {
        GreetingService greetService = message ->
                System.out.println(salutation + message);
        greetService.sayMessage("二哥!我是Lambda。");
    }

    interface GreetingService {
        void sayMessage(String message);
    }
}

运行结果:

代码语言:javascript
复制
/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/... com.sjh.test.java8.TestLambdaSecond
你好,二哥!我是Lambda。

Process finished with exit code 0

2、Lambda 引用的普通的变量也是可以的,只要这个变量没有第二次被赋值,不管是任何地方。

代码语言:javascript
复制
package com.sjh.test.java8;

public class TestLambdaThree {

    static String salutation = "你好,";

    public static void main(String args[])
    {
        GreetingService greetService = message ->
                System.out.println(salutation + message);
        greetService.sayMessage("二哥!我是Lambda。");
    }

    interface GreetingService {
        void sayMessage(String message);
    }
}

运行结果:

代码语言:javascript
复制
/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/...com.sjh.test.java8.TestLambdaThree
你好,二哥!我是Lambda。

Process finished with exit code 0

3、如果 lambda 表达式引用的是当前作用域下的普通的变量,而该变量又在某个地方第二次被赋值,则会抛出一个编译错误。

代码语言:javascript
复制
package com.sjh.test.java8;

public class TestLamdbaFour {

    public static void main(String args[])
    {
        String salutation = "你好,";

        GreetingService greetService = message ->
                System.out.println(salutation + message);
        greetService.sayMessage("二哥!我是Lambda。");
        salutation = "Hello,";
    }

    interface GreetingService {
        void sayMessage(String message);
    }
}

错误提示:

代码语言:javascript
复制
Information:java: Errors occurred while compiling module 'test'
Information:javac 1.8.0_171 was used to compile java sources
Information:2020-05-27 12:39 - Build completed with 1 error and 0 warnings in 2 s 758 ms
/Users/sunjiahao/Develop/gitee_project/test/src/com/sjh/test/java8/TestLamdbaFour.java
Error:(10, 36) java: 从lambda 表达式引用的本地变量必须是最终变量或实际上的最终变量

4、如果 lambda 表达式引用的变量并不是当前作用域下声明的,也可以随意赋值,并不会报错

代码语言:javascript
复制
package com.sjh.test.java8;

public class TestLambdaFive {

    static String salutation = "你好,";

    public static void main(String args[])
    {
        salutation = "Hello,";
        GreetingService greetService = message ->
                System.out.println(salutation + message);
        greetService.sayMessage("二哥!我是Lambda。");
        salutation = "你好,";
    }

    interface GreetingService {
        void sayMessage(String message);
    }
}

运行结果:

代码语言:javascript
复制
/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/...com.sjh.test.java8.TestLambdaFive
Hello,二哥!我是Lambda。

Process finished with exit code 0

总结:

Java lambda 表达式可以随意引用外部变量,但如果外部变量是在当前作用域声明的,则一定不可以进行第二次赋值,哪怕是在 Lambda 语句之后。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-05-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java小技巧 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档