教你如何封装自定义函数式接口

简介: 前言Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包,和传统数学上的意义有区别。【摘自百度百科】

前言

Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包,和传统数学上的意义有区别。【摘自百度百科】

  Java在JDK8的时候引入了Lambda表达式的用法,它简化了我们的代码并且可以让我们的代码更简洁,也大大的提高了我们代码的可读性。   其实我们在日常的一些工作中或多或少都有用到过Lambda表达式,例如Stream流、MybatisPlus中的一些使用等等。那么我们如何去封装自定义的Lambda表达式呢?又为何要封装自定义的Lambda表达式呢?别急,且听我娓娓道来。

必要知识

  在我们进行自定义Lambda表达式之前,应该先了解一下它的原理以及一些相关的类,这样有助于我们根据现有的业务去编写相应的代码。

Function

  使用过MP的小伙伴们应该都知道,MybatisPlus中的条件构造器中有一项叫做LambdaQueryWrapper的条件构造器,用来构造我们需要的函数,在这里我举个例子:

java复制代码    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(User::getUsername, User::getPassword);

从上面的代码我们可以看到,在 select 这个方法中,我们使用的不是传统的getter的写法,而是中间带了两个冒号,这就是lambda表达式语法糖的一种表现形式,我们点进去看看他里面长什么样。

里面的内容很简单,我们可以看到这个方法的入参是多个SFunction,那我们再点进去这个SFunction看看里面有什么。

它里面的逻辑很简单,加了一个 @FunctionalInterface 的注解,并继承了 Function ,点进去看一下

我们可以看到这个接口也加了@FunctionalInterface注解,并且接口上有两个泛型:T 和 R T表示了我们的入参类型 R表示了我们的出参类型

我们再来看接口上面的这个注解 @FunctionalInterface,不知道是啥意思,那我们就继续往里面点!

点进去之后我们发现没什么东西,但是上面有注释,借助翻译看一下

大概的意思就是说我使用了这个注解后,就可以标明该接口为一个函数式的接口,然后就可以支持Lambda表达式了

那么除了Function还有没有其他的函数式接口呢?答案是有的!我们只要进入@FunctionalInterface注解内,按住Ctrl往里面点就可以看到所有引用该注解的接口有哪些了,这里就不一一赘述了,就只捡几个比较典型的例子给大家说明一下。

Consumer

Java中引入的函数式接口可不止Function一个,另外还有Consumer,Supplier,Runnable等,别急,咱们一个一个看。

首先是Consumer这个接口,打开之后可以看到它也有一个@FunctionalInterface注解, 刚才我们已经知道了只要引用了这个注解,那么下面的接口就被声明为一个函数式接口。

但是它只有一个泛型T,并且它的方法里可以看到返回类型为void。 那么意思就很明显了,它表明了T为入参类型,没有返回参数。

Supplier

另外一个函数式接口为Supplier,我们看一下这个接口内部都写了什么。

这个函数式接口也引用了@FunctionalInterface注解,这个就不多说了,它也有一个泛型T,但是要注意的是这个泛型与上面Consumer的泛型不一样,它代表的不是入参,而是出参类型,从下面的get方法也可以看出来。

Runnable

除了上面说的同时有入参和出参的Function、只有入参的Consumer和只有出参的Supplier之外,还有一种入参和出参都没有的接口,Runnable就是其中一个典型的例子,我们找到这个类进去看一下。

表格

接下来我们做一个表格,以供我们清晰的看到我们在定义函数式接口时可选的几种类型

接口名

入参

出参

Function

Consumer

Supplier

Runnable

通过上述表格,我们可以根据我们项目中具体的业务来选择到底要用哪一种。

自定义函数式接口

我们在实际的开发工作中,经常可以看到类似的业务,比如:如果结果为false则抛出异常。我们可能会以这种形式来完成这样的业务:

java复制代码    boolean res = xxxService.add();
        if (Boolean.FALSE.equals(res)){
            throw new xxxException();
        }

但是我们可以通过Lambda表达式的方式来完成这样的业务,首先我们去编写一个工具类。

FunctionUtils

isFalse

新建一个FunctionUtils,并在里面写入下面这个方法。

java复制代码public static CustomException isFalse(boolean res) {
    return (errorMessage) -> {
        if (Boolean.FALSE.equals(res)) {
            throw new RuntimeException(errorMessage);
        }
    };
}

其中CustomException是我们自定义的一个接口,如下:

java复制代码@FunctionalInterface
public interface CustomException {
  /**
  * @param message 错误信息
  * @return 
  */
    void throwCustomException(String message);
}

然后我们在调用的时候就可以这样调用来完成我们需要的业务了。

java复制代码  boolean res = xxxService.doSomething();
    FunctionUtils.isFalse(res).throwCustomException("error");

isTrueOrFalse

当然,除了这种调用之外,还可以有一种类似于if-else的调用方法。我们在FunctionUtils中添加下面这个方法。

java复制代码public static LogicHandler isTrueOrFalse(boolean res) {
    return (trueHandler, falseHandler) -> {
        if (Boolean.TRUE.equals(res)) {
            trueHandler.run();
        } else {
            falseHandler.run();
        }
    };
}

其中LogicHandler也是我们自定义的一个函数式接口,并在其内部定义了两个无入参和出参的处理器:

java复制代码@FunctionalInterface
public interface LogicHandler {
  /**
  * @param trueHandler  成功处理器
  * @param falseHandler 失败处理器
  * @return
  */
    void logicHandler(Runnable trueHandler, Runnable falseHandler);
}

传入两个处理器,让他来帮我们处理不同的业务,我们在调用的时候就可以这样去调用:

java复制代码boolean res = xxxService.doSomething();
FunctionUtils.isTrueOrFalse(res).logicHandler(() -> {
        //To do something 
    },
    () -> {
        //To do something 
});

这样我们就可以以lambda的形式来处理我们不同的业务了。

isPresent

除此之外我们可能还会遇到一些判断对象是否为空的场景,这时候我们也可以去封装函数式接口来实现。在FunctionUtils中加入如下代码:

java复制代码public static PresentOrOtherHandler<?> isPresent(Object obj) {
    return (action, otherHandler) -> {
        if (Objects.nonNull(obj)) {
            action.accept(obj);
        } else {
            otherHandler.run();
        }
    };
}

我们在调用的时候通过如下的方式进行调用:

java复制代码FunctionUtils.isPresent(null).presentOrOtherHandler(System.out::println,
            () -> {
                //to do something
            });

如果对象不为空的话则执行传入的Lambda表达式,否则就进入到其他的逻辑中。 在这里我们传入的为System.out::println,它等同于System.out.println(obj),相当于Consumer的处理方式,我们知道Consumer是需要一个入参的,而System.out::println就相当于我们的入参。

那么上面这段代码可以解释为:如果对象非空的话就执行传入的方法,否则执行定义好的逻辑。

总结

我们在封装函数式接口时总共需要用到Function、Consumer、Supplier和Runnable四种方式进行调用,而这四种方式各有各的特点,我们也可以根据自己的业务场景来选择不同的接口去封装。

封装函数式接口可以使我们的代码更加优雅和简洁,提高了我们代码的可读性,但是我们也不能无脑的去使用该方法,遇到什么样的接口都使用函数式的接口进行封装,还是要 根据实际的业务场景来选择 是否要封装自定义的函数式接口。

作者:Bummon

链接:
https://juejin.cn/post/7262396489117089848

相关文章
|
4月前
|
JavaScript API
接口的封装
接口的封装
|
11月前
|
Java
类与接口介绍
在Java中,类和接口是两种重要的概念,用于描述对象的属性和行为。它们是面向对象编程的基础,用于组织和管理代码。 类(Class)是一种模板或蓝图,用于创建对象。它定义了对象的属性和行为。类是Java中最基本的组织单元,所有的对象都是根据类来创建的。类由字段(属性)和方法组成。字段表示对象的状态或属性,而方法表示对象的行为或操作。 以下是一个简单的Java类的示例: ```java public class Person { // 字段 private String name; private int age; // 构造方法 publi
52 0
|
4月前
|
Java
Java接口中可以定义哪些方法?
【4月更文挑战第13天】
182 0
Java接口中可以定义哪些方法?
|
4月前
|
存储 算法 C++
C++11:lambda表达式 & 包装器
C++11:lambda表达式 & 包装器
27 0
|
4月前
|
测试技术 API
(接口封装)
(接口封装)
76 0
|
编译器 C++
【C++11】lambda表达式 包装器
【C++11】lambda表达式 包装器
50 0
函数式接口概述、作为方法的参数、作为方法的返回值及函数式接口Supplier介绍
函数式接口概述、作为方法的参数、作为方法的返回值及函数式接口Supplier介绍
63 0
|
SQL 关系型数据库 数据库连接
接口继承|学习笔记
快速学习接口继承,接口只能被类去实现,如果想要规范团队,也不能把所有想要规范的东西写在一个接口里,这样会使接口臃肿,不好实现接口的类,因为有可能并不需要这么多规范。为了使接口的体系更加完整,更加具有结构性,就有了接口继承。本节的目标是理解接口的继承体系,掌握具体的接口继承实现。
接口继承|学习笔记
|
网络协议 测试技术 Go
实现接口和继承比较(2) | 学习笔记
快速学习实现接口和继承比较(2)
实现接口和继承比较(2) | 学习笔记