Java新特性:Lambda表达式

2023年 7月 14日 40.7k 0

1、Lambda表达式概述

1.1、Lambda表达式的简介

Lambda 表达式(Lambda expression),也可称为闭包(Closure),是 Java(SE)8 中一个重要的新特性。

Lambda 表达式允许我们通过表达式来代替功能接口。Lambda 表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)

Lambda 表达式可以看作是一个匿名函数,基于数学中的 λ 演算得名。

1.2、Lambda表达式的语法

Lambda 表达式的基本语法:

(parameters) -> expression 或 (parameters) -> { statements; }

1

Lambda 表达式的三个组成部分:

image-20230707105839045

  • 参数列表(parameters):类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明,也可不声明而由 JVM 隐含的推断。另外当只有一个推断类型时可以省略掉圆括号;
  • 箭头(->):连接参数列表和 Lambda 主体,可理解为"被用于"的意思;
  • Lambda 主体(expression 或 { statements; }):可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不反回,这里的代码块块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不反回。
  • Lambda 表达式样例:

            // 1. 不需要参数,返回值为 2
            () -> 2
    				// 2. 接收一个参数(数字类型),返回其2倍的值
            x -> 2*x
    				// 3. 接受2个参数(数字),并返回他们的和
            (x, y) -> x+y
    				// 4. 接收2个int型整数,返回他们的乘积
            (int x, int y) -> x*y
    				// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
            (String s) -> System.out.print(s)
    
    12345678910
    
    1.3、Lambda表达式的要求

    虽然说,Lambda 表达式可以在⼀定程度上简化接口的实现。但是,并不是所有的接口都可以使用 Lambda 表达式来简洁实现的。

    Lambda 表达式毕竟只是⼀个匿名方法。当实现的接口中的方法过多或者多少的时候,Lambda 表达式都是不适用的。

    Lambda 表达式,只能实现函数式接口。

    2、函数式接口

    2.1、什么是函数式接口

    如果说,⼀个接口中,要求实现类必须实现的抽象方法,有且只有⼀个!这样的接口,就是函数式接口。

    代码如下(示例):

    // 有且只有一个实现类必须要实现的抽象方法,所以是函数式接口
    interface Demo{
        public void action();
    }
    
    1234
    

    或者(示例):

    // 有且只有一个实现类必须要实现的抽象方法,所以是函数式接口
    interface Demo{
        public void action();
        public default void action2() {
            System.out.println("JDK1.8新特性,default默认方法可以有具体的实现");
        }
    }
    
    1234567
    
    2.2、函数式接口注解(@FunctionalInterface)

    在接口之前,我们可以使用 @FunctionalInterface 注解来判断这个接口是否是⼀个函数式接口。,用如果是函数式接口,没有任何问题。如果不是函数式接口,则会报错。功能类似于 @Override
    代码如下(示例):

    @FunctionalInterface
    interface Demo{
        public void action();
    }
    
    1234
    

    3、Lambda表达式的使用

    3.1、Lambda表达式的基本使用

    例1(无参数无返回值):

    @FunctionalInterface
    interface NoParameterNoReturn {
        // 注意:只能有一个抽象方法
        void action();
    }
    public class TestDemo {
        public static void main(String[] args) {
            NoParameterNoReturn noParameterNoReturn = () -> {
                System.out.println("无参数无返回值");
            };
            // action方法的主体内容在上述括号内
            noParameterNoReturn.action(); 
         }
    }
    
    1234567891011121314
    

    例2(一参数无返回值):

    @FunctionalInterface
    interface OneParameterNoReturn {
      	// 注意:只能有一个抽象方法
        void action(int a);
    }
    public class TestDemo {
        public static void main(String[] args) {
            OneParameterNoReturn oneParameterNoReturn = (int a) -> {
                System.out.println("一参数无返回值:" + "参数1:" + a);
            };
            // action方法的主体内容在上述括号内
            oneParameterNoReturn.action(10);
         }
    }
    
    1234567891011121314
    

    例3(多参数无返回值):

    @FunctionalInterface
    interface MoreParameterNoReturn {
      	// 注意:只能有一个抽象方法
        void action(int a, int b);
    }
    public class TestDemo {
        public static void main(String[] args) {
    						MoreParameterNoReturn moreParameterNoReturn = (int a, int b) -> {
                System.out.println("多参数无返回值:" + "参数1:" + a  + "参数2:" + b);
            };
            moreParameterNoReturn.action(20, 30);
         }
    }
    
    12345678910111213
    

    例4(无参数有返回值):

    @FunctionalInterface
    interface NoParameterReturn {
        int action();
    }
    public class TestDemo {
        public static void main(String[] args) {
            NoParameterReturn noParameterReturn = () -> {
                System.out.println("无参数有返回值");
                return 40;
            };
            //接收函数的返回值
            int ret = noParameterReturn.action();
            System.out.println(ret);
         }
    }
    
    123456789101112131415
    

    例5(一参数有返回值):

    //有返回值一个参数
    @FunctionalInterface
    interface OneParameterReturn {
        int action(int a);
    }
    public class TestDemo {
        public static void main(String[] args) {
            OneParameterReturn oneParameterReturn = (int a) -> {
                System.out.println("一参数有返回值");
                return a;
            };
            ret = oneParameterReturn.action(50);
            System.out.println(ret);
         }
    }
    
    123456789101112131415
    

    例6(多参数有返回值):

    @FunctionalInterface
    interface MoreParameterReturn {
        int action(int a, int b);
    }
     
    public class TestDemo {
        public static void main(String[] args) {
            MoreParameterReturn moreParameterReturn = (int a, int b) -> {
                System.out.println("多参数有返回值");
                return a + b;
            };
            ret = moreParameterReturn.action(60, 70);
            System.out.println(ret);
         }
    }
    
    123456789101112131415
    
    3.2、Lambda表达式的语法精简

    Lambda 表达式的语法精简:

    • 参数类型可以省略,如果需要省略,每个参数的类型都要省略;
    • 参数的小括号里面只有一个参数,那么小括号可以省略;
    • 如果方法体当中只有一句代码,那么大括号可以省略;
    • 如果方法体中只有一条语句,其是 return 语句,那么大括号可以省略,且去掉 return 关键字。

    4、变量捕获

    Lambda 表达式中存在变量捕获,了解了变量捕获之后,我们才能更好的理解 Lambda 表达式的作用域。Java 当中的匿名类中,会存在变量捕获。

    4.1、匿名内部类的变量捕获

    所谓变量捕获就是:匿名内部类中访问所在方法或代码块中的局部变量,这个过程被称为 “变量捕获”。

    public static void main(String[] args) {
        // 变量捕获
        int a = 100;
        NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() {
            @Override
            public void action() {
                // 捕获a变量
                System.out.println(a);   
            }
        };
        noParameterNoReturn.action();
    }
    结果:100
    
    12345678910111213
    

    但是,只有在局部变量是 “有效的最终变量” 时,匿名内部类才能够引用它。如果局部变量不是有效的最终变量,则无法在匿名内部类中引用它,会导致编译错误。

    有效的最终变量指的是一个在生命周期中没有被修改过的局部变量,它可以被认为是一个常量。

    4.2、Lambda的变量捕获

    同样的,Lambda 的变量捕获也是只有在局部变量是 “有效的最终变量” 时,才能够引用它。

    public static void main(String[] args) {
        // 变量捕获
        int a = 100;
        NoParameterNoReturn noParameterNoReturn = ()->{
            // 捕获a变量
            System.out.println(a);
        };
        noParameterNoReturn.action();
    }
    结果:100
    
    12345678910
    
    4.3、匿名内部类的变量捕获与Lambda的变量捕获的区别

    区别:Lambda 表达式可以捕获外面的 this,而匿名内部类无法直接捕获外面的 this。

    在 Lambda 表达式中,this 关键字引用的是 Lambda 表达式所在的类的实例,而不是 Lambda 表达式内部的类的实例。这个特性被称为 “闭包”。

    匿名内部类中的 this 关键字引用的是匿名内部类的实例,而不是外部类的实例。

    public class Student {
        // 外部类的属性
        String name = "Student";
        int age;
     
        NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() {
            // 内部类自己的属性
            String name = "hello";
            @Override
            public void action() {
                System.out.println(this.name);
            }
        };
     
        NoParameterNoReturn noParameterNoReturn2 = ()->{
            String name = "hello";
            System.out.println(this.name);
        };
     
        public static void main(String[] args) {
            Student student = new Student();
          	// 结果:hello
            student.noParameterNoReturn.action();
          	// 结果:Student
            student.noParameterNoReturn2.action();
        }
    }
    
    123456789101112131415161718192021222324252627
    

    如果想让匿名内部类的捕获外面的 this,就需要使用外部类的类名或对象引用来访问。

    public class Student {
        // 外部类的属性
        String name = "Student";
        int age;
     
         NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() {
            // 内部类自己的属性
            String name = "hello";
            @Override
            public void action() {
                System.out.println(Student.this.name);
            }
        };
    }
    结果:Student
    
    123456789101112131415
    

    5、Lambda 在集合当中的使用

    为了能够让 Lambda 和 Java 的集合类集更好的一起使用,集合当中,也新增了部分接口,以便与 Lambda 表达式对接。

    对应的接口 新增的方法
    Collection forEach() removeIf() spliterator() stream() parallelStream()
    List replaceAll() sort()
    Map getOrDefault() forEach() replaceAll() putIfAbsent() remove() replace() computeIfAbsent() computeIfPresent() compute() merge()
    5.1、Collection接口中的forEach()方法

    这个方法是在接口 Iterable 当中的:

        default void forEach(Consumer

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论