Java基础 第五节 第五课

举报
我是小白呀iamarookie 发表于 2021/09/10 01:24:28 2021/09/10
【摘要】 函数式编程 概述Lambda 的延迟执行性能浪费的日志案例体验 Lambda 的更优写法证明 Lambda 的延迟 使用 Lambda 作为参数和返回值 概述 在兼顾面向对象特性的基础...

概述

在兼顾面向对象特性的基础上, Java 语言通过 Lambda 表达式与方法引用等, 为开发者打开了函数式编程的大门.

Lambda 的延迟执行

有些场景的代码执行后, 结果不一定会被使用, 从而造成性能浪费. 而 Lambda 表达式是延时执行的, 这正好可以作为解决方案, 提升性能.

性能浪费的日志案例

日志可以帮助我们快速的定位问题, 记录程序运行过程中的情况, 以便项目的监控和优化.

一种典型的场景就是对参数进行有条件使用. 例如对日志消息进行拼接后, 在满足条件的情况下进行打印输出:

public class Logger {
    private static void log(int level, String msg) {
        if (level == 1) {
            System.out.println(msg);
        }
    }

    public static void main(String[] args) {
        String msgA = "Hello";
        String msgB = "World";
        String msgC = "Java";

        log(1, msgA + msgB + msgC);
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这段代码存在的问题:

无论级别是否满足要求, 作为 log 方法的第二个参数, 三个字符串一定会首先被拼接并传入方法内, 然后才会进行级别判断. 如果级别不符合要求, 那么字符串的拼接操作就白做了, 存在性能浪费.

体验 Lambda 的更优写法

使用 Lambda 必然需要一个函数式接口:

@FunctionalInterface
public interface MessageBuilder {
    String buildMessage();
}

  
 
  • 1
  • 2
  • 3
  • 4

然后对 log 方法进行改造:

public class Test {
    private static void log(int level, MessageBuilder builder){
        if(level == 1){
            System.out.println(builder.buildMessage());
        }
    }
    public static void main(String[] args) {
        String msgA = "Hello";
        String msgB = "World";
        String msgC = "Java";

        log(1, () -> msgA + msgB + msgC);
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

这样一来, 只有当级别满足要求的时候, 才会进行三个字符串的拼接. 否则三个字符串将不会进行拼接.

证明 Lambda 的延迟

下面的代码可以通过结果进行验证:

public class Test {
    private static void log(int level, MessageBuilder builder) {
        if (level == 0) {
            System.out.println(builder.buildMessage());
        }
    }

    public static void main(String[] args) {
        String msgA = "Hello";
        String msgB = "World";
        String msgC = "Java";

        log(1, () -> {
            System.out.println("Lambda 执行!");
            return msgA + msgB + msgC;
        });
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

从结果中可以看出, 在不符合级别的情况下, Lambda 将不会执行. 从而达到节省性能的效果.

扩展:

实际上使用内部类也可以达到同样的效果. 只是将代码操作延迟到了另外一个对象当中通过调用方法来完成. 而是否调用其所在方法是在条件判断之后才执行的.

使用 Lambda 作为参数和返回值

如果抛开实现原理不说, Java 中的 Lambda 表达式可以被当做是匿名内部类的替代品. 如果方法的参数是一个函数式接口类型, 那么就可以使用 Lambda 表达式进行替代. 使用 Lambda 表达作为方法参数, 其实就是使用函数式接口作为方法参数.

例如java.lang.Runnable接口就是一个函数式接口. 假设有一个 startThread 方法使用该接口作为参数, 那么就可以使用 Lambda 进行传参. 这种情况其实和 Thread 类的构造方法参数为 Runnable 没有本质区别.

public class Test {
    private static void startThread(Runnable task) {
        new Thread(task).start();
    }

    public static void main(String[] args) {
        startThread(() -> System.out.println("线程任务执行 !"));
    }
}

输出结果:
"线程任务执行 !"

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

类似地, 如果一个方法的返回值类型是一个函数式接口, 那么就可以直接返回一个 Lambda 表达式. 当需要通过一个方法来获取一个```java.util,Comparator``接口类型的对象作为排序器时, 就可以调用该方法获取.

import java.util.Arrays;
import java.util.Comparator;

public class Test {
    private static Comparator<String> Compare() {
        return (a, b) -> b.length() - a.length();
    }

    public static void main(String[] args) {
        String[] array = {"abc", "ab", "abcd"};
        System.out.println(Arrays.toString(array));
        Arrays.sort(array, Compare());
        System.out.println(Arrays.toString(array));
    }
}

输出结果:
[abc, ab, abcd]
[abcd, abc, ab]

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

其中直接 return 一个 Lambda 表达式即可.

文章来源: iamarookie.blog.csdn.net,作者:我是小白呀,版权归原作者所有,如需转载,请联系作者。

原文链接:iamarookie.blog.csdn.net/article/details/111777603

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。