前语

Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式依据数学中的演算得名,直接对应于其间的lambda笼统(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式能够表明闭包,和传统数学上的意义有差异。【摘自百度百科】

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

都2023年了还不会封装自定义函数式接口?一文教会你

必要常识

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

Function

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

		LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(User::getUsername, User::getPassword);

从上面的代码咱们能够看到,在 select 这个办法中,咱们运用的不是传统的getter的写法,而是中间带了两个冒号,这便是lambda表达式语法糖的一种表现办法,咱们点进去看看他里边长什么样。

都2023年了还不会封装自定义函数式接口?一文教会你

里边的内容很简单,咱们能够看到这个办法的入参是多个SFunction,那咱们再点进去这个SFunction看看里边有什么。

都2023年了还不会封装自定义函数式接口?一文教会你

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

都2023年了还不会封装自定义函数式接口?一文教会你

咱们能够看到这个接口也加了@FunctionalInterface注解,而且接口上有两个泛型:TR T表明了咱们的入参类型 R表明了咱们的出参类型

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

都2023年了还不会封装自定义函数式接口?一文教会你

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

都2023年了还不会封装自定义函数式接口?一文教会你

都2023年了还不会封装自定义函数式接口?一文教会你

大约的意思便是说我运用了这个注解后,就能够标明该接口为一个函数式的接口,然后就能够支持Lambda表达式了

都2023年了还不会封装自定义函数式接口?一文教会你
那么除了Function还有没有其他的函数式接口呢?答案是有的!咱们只需进入@FunctionalInterface注解内,按住Ctrl往里边点就能够看到一切引证该注解的接口有哪些了,这里就不逐个赘述了,就只捡几个比较典型的例子给大家说明一下。

Consumer

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

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

都2023年了还不会封装自定义函数式接口?一文教会你
可是它只要一个泛型T,而且它的办法里能够看到回来类型为void。 那么意思就很显着了,它表明了T入参类型,没有回来参数。

Supplier

别的一个函数式接口为Supplier,咱们看一下这个接口内部都写了什么。

都2023年了还不会封装自定义函数式接口?一文教会你
这个函数式接口也引证了@FunctionalInterface注解,这个就不多说了,它也有一个泛型T,可是要注意的是这个泛型与上面Consumer的泛型不一样,它代表的不是入参,而是出参类型,从下面的get办法也能够看出来。

Runnable

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

都2023年了还不会封装自定义函数式接口?一文教会你

表格

接下来咱们做一个表格,以供咱们明晰的看到咱们在界说函数式接口时可选的几种类型

接口名 入参 出参
Function
Consumer
Supplier
Runnable

经过上述表格,咱们能够依据咱们项目中详细的事务来挑选究竟要用哪一种。

自界说函数式接口

咱们在实践的开发工作中,经常能够看到类似的事务,比方:假如结果为false则抛出反常。咱们可能会以这种办法来完结这样的事务:

		boolean res = xxxService.add();
        if (Boolean.FALSE.equals(res)){
            throw new xxxException();
        }

可是咱们能够经过Lambda表达式的办法来完结这样的事务,首先咱们去编写一个工具类。

FunctionUtils

isFalse

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

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

其间CustomException是咱们自界说的一个接口,如下:

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

然后咱们在调用的时分就能够这样调用来完结咱们需求的事务了。

	boolean res = xxxService.doSomething();
    FunctionUtils.isFalse(res).throwCustomException("error");

isTrueOrFalse

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

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

其间LogicHandler也是咱们自界说的一个函数式接口,并在其内部界说了两个无入参和出参的处理器:

@FunctionalInterface
public interface LogicHandler {
	/**
	* @param trueHandler  成功处理器
	* @param falseHandler 失利处理器
	* @return
	*/
    void logicHandler(Runnable trueHandler, Runnable falseHandler);
}

传入两个处理器,让他来帮咱们处理不同的事务,咱们在调用的时分就能够这样去调用:

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

这样咱们就能够以lambda的办法来处理咱们不同的事务了。

isPresent

除此之外咱们可能还会遇到一些判别目标是否为空的场景,这时分咱们也能够去封装函数式接口来完成。在FunctionUtils中参加如下代码:

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

咱们在调用的时分经过如下的办法进行调用:

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

假如目标不为空的话则履行传入的Lambda表达式,不然就进入到其他的逻辑中。 在这里咱们传入的为System.out::println,它等同于System.out.println(obj),相当于Consumer的处理办法,咱们知道Consumer是需求一个入参的,而System.out::println就相当于咱们的入参。

那么上面这段代码能够解释为:假如目标非空的话就履行传入的办法,不然履行界说好的逻辑。

总结

咱们在封装函数式接口时总共需求用到FunctionConsumerSupplierRunnable四种办法进行调用,而这四种办法各有各的特色,咱们也能够依据自己的事务场景来挑选不同的接口去封装。

封装函数式接口能够使咱们的代码愈加高雅和简洁,提高了咱们代码的可读性,可是咱们也不能无脑的去运用该办法,遇到什么样的接口都运用函数式的接口进行封装,还是要 依据实践的事务场景来挑选 是否要封装自界说的函数式接口。


推荐

重视博客和公众号获取最新文章

Bummon’s Blog | Bummon’s Home | 公众号

都2023年了还不会封装自定义函数式接口?一文教会你