gpt4 book ai didi

Javalambda表达式基本使用

转载 作者:我是一只小鸟 更新时间:2022-11-26 14:31:19 38 4
gpt4 key购买 nike

代码示例: java.lambda.LambdaExpression 。

1 本质

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

1.1 案例

有以下 List<Integer> 对象:

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

                        
                      

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

                        
                          default void sort(Comparator<? super E> c) {
    // 省略方法体
}

                        
                      

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

1、 创建 Comparator 的实现类

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

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

                        
                      

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

                        
                          Comparator<Integer> ascComparator = new AscComparator();
list.sort(ascComparator);

                        
                      

2、创建 Comparator 的匿名对象

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

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

                        
                      

等价于:

                        
                          list.sort(new Comparator<Integer>() {
    @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<T> {
        void accept(T t);
    }

    interface AcceptMore<T, E> {
        void accept(T t, E e);
    }

    interface ReturnVoid {
        void returnVoid();
    }

    interface ReturnR<R> {
        R returnR();
    }
}

                        
                      
  • 定义调用类,接收不同的 lambda 表达式:
                        
                          /**
* 调用函数式接口的服务类
* @param <T> 第一个形参类型
* @param <E> 第二个形参类型
* @param <R> 返回值类型
*/
public class Service<T, E, R> {
    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<T> acceptOne) {
        acceptOne.accept(this.t);
    }

    void acceptMore(FunctionInterface.AcceptMore<T, E> acceptMore) {
        acceptMore.accept(this.t, this.e);
    }

    void returnVoid(FunctionInterface.ReturnVoid returnVoid) {
        returnVoid.returnVoid();
    }

    R returnR(FunctionInterface.ReturnR<R> returnR) {
        return returnR.returnR();
    }
}

                        
                      
  • 创建服务类实例:
                        
                          Service<Integer, Integer, String> 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<Integer>() {
    @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<Integer, Integer>() {
    @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<String>() {
    @Override
    public String returnR() {
        return "3";
    }
});
service.returnR(() -> "3");

                        
                      

3 执行逻辑

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

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

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

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

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

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

  • 定义一个函数式接口:
                        
                          public interface Lambda<T, R> {
    R method(T t);
}

                        
                      
  • 定义调用类:
                        
                          public class FakeFunctionalProgramming<T, R> {
    private T t;
    private Lambda<T, R> lambda;

    public void setT(T t) {
        this.t = t;
    }

    public void setLambda(Lambda<T, R> 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<String, String> ffp = new FakeFunctionalProgramming<>();
ffp.setT("Xianhuii");
ffp.setLambda((t) -> "Hello " + t + "!");
ffp.doSomeThing();  // Hello Xianhuii!

                        
                      

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

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

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

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

最后此篇关于Javalambda表达式基本使用的文章就讲到这里了,如果你想了解更多关于Javalambda表达式基本使用的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

38 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com