• Java lambda表达式基本使用


    代码示例:java.lambda.LambdaExpression

    1 本质#

    • lambda表达式本质上是对匿名内部类实例的一种简化写法。

    1.1 案例#

    有以下List对象:

    List list = Arrays.asList(1, 3, 5, 7, 9, 2, 4, 6, 8, 10);
    

    在对List进行从小大大排序时,会用到List#sort(Comparator)方法,需要传递实现Comparator接口的对象作为参数:

    default void sort(Comparatorsuper E> c) {
        // 省略方法体
    }
    

    可以想到有如下四种不同的代码编写的方式。

    1、 创建Comparator的实现类#

    根据需求,手动实现Comparator接口:

    public class AscComparator implements Comparator {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1.compareTo(o2);
        }
    }
    

    然后,创建AscComparator实例,传给List#sort(Comparator)方法:

    Comparator ascComparator = new AscComparator();
    list.sort(ascComparator);
    

    2、创建Comparator的匿名对象#

    可以直接创建Comparator的匿名对象,然后传给List#sort(Comparator)方法:

    Comparator anonymousComparator = new Comparator() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1.compareTo(o2);
        }
    };
    list.sort(anonymousComparator);
    

    等价于:

    list.sort(new Comparator() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1.compareTo(o2);
        }
    });
    

    3、lambda表达式#

    直接使用lambda表达式:

    list.sort((o1, o2) -> o1.compareTo(o2));
    

    4、方法引用#

    使用方法引用(方法引用具体概念和使用可以查看相关文章):

    list.sort(Integer::compare);
    

    可以明显看出,使用lambda表达式和方法引用极大提高了开发的速度,提升了代码的简洁性。

    1.2 本质#

    实际上,lambda表达式只是JVM提供的语法糖。在JVM执行过程中,会根据lambda表达式的规则,动态创建出匿名的接口实现类对象。

    • lambda表达式本质上是Java对象。

    可以通过查看lambda表达式的Class对象和实例对象来证明这一点:

    public class LambdaExpression {
        public void printConsumer(Consumer consumer) {
            System.out.println(consumer.getClass());
            System.out.println(consumer.getClass().getInterfaces()[0]);
            System.out.println(consumer);
        }
    }
    

    1、案例1#

    运行以下代码:

    LambdaExpression lambdaObjPrinter = new LambdaExpression();
    lambdaObjPrinter.printConsumer(o -> o.getClass());
    lambdaObjPrinter.printConsumer(o -> o.getClass());
    

    会有如下输出:

    class lambda.LambdaExpression$$Lambda$1/2003749087
    interface java.util.function.Consumer
    lambda.LambdaExpression$$Lambda$1/2003749087@41629346
    class lambda.LambdaExpression$$Lambda$2/1078694789
    interface java.util.function.Consumer
    lambda.LambdaExpression$$Lambda$2/1078694789@6d311334
    
    • 这证明了执行过程中会根据lambda表达式动态生成函数式接口的实现类,并创建该实现类的实例。
    • 同时,先后执行的2个lambda表达式,尽管格式相同,仍然动态生成了2个实现类。

    查看编译后的.class文件如下:

    LambdaExpression lambdaObjPrinter = new LambdaExpression();
    lambdaObjPrinter.printConsumer((o) -> {
        o.getClass();
    });
    lambdaObjPrinter.printConsumer((o) -> {
        o.getClass();
    });
    

    2、案例2#

    运行如下代码:

    LambdaExpression lambdaObjPrinter = new LambdaExpression();
    for (int i = 0; i < 2; i++) {
        lambdaObjPrinter.printConsumer(o -> o.getClass());
    }
    System.out.println("=============");
    for (int i = 0; i < 2; i++) {
        lambdaObjPrinter.printConsumer(o -> o.getClass());
    }
    

    会发现有如下输出:

    class lambda.LambdaExpression$$Lambda$1/2003749087
    interface java.util.function.Consumer
    lambda.LambdaExpression$$Lambda$1/2003749087@41629346
    class lambda.LambdaExpression$$Lambda$1/2003749087
    interface java.util.function.Consumer
    lambda.LambdaExpression$$Lambda$1/2003749087@41629346
    =============
    class lambda.LambdaExpression$$Lambda$2/1078694789
    interface java.util.function.Consumer
    lambda.LambdaExpression$$Lambda$2/1078694789@6d311334
    class lambda.LambdaExpression$$Lambda$2/1078694789
    interface java.util.function.Consumer
    lambda.LambdaExpression$$Lambda$2/1078694789@6d311334
    
    • 说明在不同for循环中(while等循环结果相同),只会动态生成1个实现类。

    查看编译后的.class文件如下:

    LambdaExpression lambdaObjPrinter = new LambdaExpression();
    
    int i;
    for(i = 0; i < 2; ++i) {
        lambdaObjPrinter.printConsumer((o) -> {
            o.getClass();
        });
    }
    
    System.out.println("=============");
    
    for(i = 0; i < 2; ++i) {
        lambdaObjPrinter.printConsumer((o) -> {
            o.getClass();
        });
    }
    
    • 说明这不是编译器编译的结果,应该是JVM执行时对循环语句中lambda表达式的优化。

    2 基本语法#

    lambda表达式本质上是对函数式接口的匿名实现类实例的一种简化写法:方法格式和lambda表达式格式一一对应。

    对于执行逻辑而言,方法主要由两部分组成(没有返回值):形参和方法体。

    lambda表达式与之对应:
    1、形参:(t1, t2[, ……]),对应方法的形参(T1 t1, T2 t2[, ……])
    2、箭头:->,固定
    3、方法体:{},对应方法的方法体

    2.1 分类#

    根据方法形参和返回值的不同组合,lambda表达式可以分成以下几类:

    1. 没有形参:
    () -> {
    	// 方法体
    }
    
    1. 一个形参:
    (t) -> {
    	// 方法体
    }
    
    1. 多个形参:

      (t1, t2[, ……]) -> {
      // 方法体
      }

    2. 没有返回值:

    () -> {
    	// 方法体
    }
    
    1. 有返回值:
    () -> {
    	// 方法体
    	return something;
    }
    

    根据形参个数的不同,形参部分可以有不同的写法:
    1、没有形参或多个形参(超过1个),需要带()

    () -> {
    	// 方法体
    }
    (t1, t2[, ……]) {
    	// 方法体
    }
    

    2、一个形参,可以省略()

    (t) -> {
    	// 方法体
    }
    t -> {
    	// 方法体
    }
    

    根据方法体中代码行数的不同,方法体部分也有不同的写法:
    1、一行代码,可以省略{}(此时该行代码的return;也必须省略):

    () -> {
    	System.out.println("Hello World!");
    }
    () -> System.out.println("Hello World!")
    () -> {
    	return "Hello World!"
    }
    () -> "Hello World!"
    

    2、多行代码,不可以省略{}

    () -> {
    	System.out.println("Hello");
    	System.out.println("World!");
    }
    () -> {
    	System.out.println("Hello");
    	return "Hello World!"
    }
    

    2.2 案例#

    • 定义函数式接口,模拟不同类型的lambda表达式:
    public class FunctionInterface {
        interface AcceptEmpty {
            void accept();
        }
    
        interface AcceptOne {
            void accept(T t);
        }
    
        interface AcceptMore {
            void accept(T t, E e);
        }
    
        interface ReturnVoid {
            void returnVoid();
        }
    
        interface ReturnR {
            R returnR();
        }
    }
    
    • 定义调用类,接收不同的lambda表达式:
    /**
    * 调用函数式接口的服务类
    * @param  第一个形参类型
    * @param  第二个形参类型
    * @param  返回值类型
    */
    public class Service {
        private T t;
        private E e;
    
        public Service(T t, E e) {
            this.t = t;
            this.e = e;
        }
    
        void acceptEmpty(FunctionInterface.AcceptEmpty acceptEmpty) {
            acceptEmpty.accept();
        }
    
        void acceptOne(FunctionInterface.AcceptOne acceptOne) {
            acceptOne.accept(this.t);
        }
    
        void acceptMore(FunctionInterface.AcceptMore acceptMore) {
            acceptMore.accept(this.t, this.e);
        }
    
        void returnVoid(FunctionInterface.ReturnVoid returnVoid) {
            returnVoid.returnVoid();
        }
    
        R returnR(FunctionInterface.ReturnR returnR) {
            return returnR.returnR();
        }
    }
    
    • 创建服务类实例:
    Service service = new Service<>(1, 2);
    

    1、没有形参#

    service.acceptEmpty(new FunctionInterface.AcceptEmpty() {
        @Override
        public void accept() {
            System.out.println("没有形参");
        }
    });
    service.acceptEmpty(() -> {
        System.out.println("没有形参");
    });
    service.acceptEmpty(() -> System.out.println("没有形参"));
    

    2、一个形参#

    service.acceptOne(new FunctionInterface.AcceptOne() {
        @Override
        public void accept(Integer t) {
            System.out.println(t);
        }
    });
    service.acceptOne((t) -> System.out.println(t));
    service.acceptOne(t -> System.out.println(t));
    

    3、多个形参#

    service.acceptMore(new FunctionInterface.AcceptMore() {
        @Override
        public void accept(Integer t, Integer e) {
            System.out.println(t);
            System.out.println(e);
        }
    });
    service.acceptMore((t, e) -> {
        System.out.println(t);
        System.out.println(e);
    });
    

    4、没有返回值#

    service.returnVoid(new FunctionInterface.ReturnVoid() {
        @Override
        public void returnVoid() {
            System.out.println("没有返回值");
        }
    });
    service.returnVoid(() -> System.out.println("没有返回值"));
    

    5、有返回值#

    service.returnR(new FunctionInterface.ReturnR() {
        @Override
        public String returnR() {
            return "3";
        }
    });
    service.returnR(() -> "3");
    

    3 执行逻辑#

    • lambda表达式本质上是传递了一个动态生成的匿名对象,是一种假的函数式编程。

    lambda表达式形式上看起来很像是函数式编程:将一个函数当作形参传给方法。

    实际上,lambda表达式只是Java的一个语法糖,它本质上仍然是一个普通的Java对象。

    在执行的过程中,lambda表达式最终还是会被解析成匿名的接口实现类对象。

    由于多态特性,在执行过程中,调用是外部传进来的实现类实例的代码。

    在这个过程中,我们甚至可以将该匿名对象保存起来,便于后续多次调用。

    • 定义一个函数式接口:
    public interface Lambda {
        R method(T t);
    }
    
    • 定义调用类:
    public class FakeFunctionalProgramming {
        private T t;
        private Lambda lambda;
    
        public void setT(T t) {
            this.t = t;
        }
    
        public void setLambda(Lambda lambda) {
            this.lambda = lambda;
        }
    
        public void doSomeThing() {
            T t = before();
            R r = lambda.method(t);
            after(r);
        }
    
        public T before() {
            return t;
        }
        public void after(R r) {
            System.out.println(r);
        }
    }
    
    • 执行以下代码:
    FakeFunctionalProgramming ffp = new FakeFunctionalProgramming<>();
    ffp.setT("Xianhuii");
    ffp.setLambda((t) -> "Hello " + t + "!");
    ffp.doSomeThing();  // Hello Xianhuii!
    

    从上述结果可以看出,lambda表达式的编程方式本质上是利用了多态的特性,同时又使用了模板方法模式:

    • 调用处接收一个接口实例Lambda作为形参。
    • 执行before()方法,处理相对固定的前处理逻辑。
    • 将执行过程中相关值作为形参传给Lambda实例,进行特定处理。
    • 接收Lambda特定处理后的返回值。
    • 执行after()方法,处理相对固定的后处理逻辑。

    此时,我们应该能够透彻理解lambda表达式中形参的来源返回值的去向了。

    借助Java多态特性,以及JVM动态生成匿名实现类实例的功能,lambda表达式才表现得那么像是函数式编程。

  • 相关阅读:
    CentOS安装Elasticsearch集群
    在MyBatis中自定义JsonTypeHandler
    moviepy第一天|模糊视频中卓别林的头,并添加一个文本生成的结尾clip,同时保留音频
    pytest-fixture固件的使用
    【数据结构】B树,B+树,B*树
    day 1 学习MySQL数据库
    二分查找详解
    再谈23种设计模式(3):行为型模式(学习笔记)
    Github Action Flask 应用CI/CD样例
    文件转换,简简单单,pdf转word,不要去找收费的了,自己学了之后免费转,之后就复制粘贴就ok了
  • 原文地址:https://www.cnblogs.com/Xianhuii/p/16927003.html