其实自java8新增了函数式接口这个抽象的特性之后,不仅仅是提供的核心函数式接口便捷了很多开发逻辑(比如:Predicate<T>,Consumer<T>,Function<T,R>,Supplier<T>),还有新增的方法引用(比如你可以在流里StringUtils::hasText)也大大减少了lambda的理解耗费。那么如果有相关了解的读者应该对这个注解很熟悉@FunctionalInterface就会知道编译器会将打上此注解的接口默认为函数式接口,笔者本篇文章就是使用函数式接口与lambda来实现函数式编程。
什么是函数式接口
笔者不喜欢复制粘贴,读者也不需要全部相信,相互对比就好,笔者认为java开发中很多地方并不符合函数式编程,对于入参与出参的过程,java开发中通常会给某个方法传参,如果仅仅是查询或者新增倒也无所谓,但是更多时候会在方法传参链式处理,一个对象可能经过多个方法之后它就与入参之前大相径庭,甚至是面目全非。但是函数式思想就是入参与出参并不影响参数本身,作为一个函数就不能影响到函数外部的变量或对象。那么函数式接口就是为了java实现函数式编程而设计的,而与JavaScript相同,Java的函数式编程也有闭包,作用域等概念。
怎么玩函数式接口
很简单,我们首先明确两点:1、被打上@FunctionalInterface的接口会默认成为函数式接口;2、只有一个抽象方法(默认方法不限)的接口自动成为一个函数式接口。
笔者将抛出demo代码来与读者分享怎么玩:
1、构定义单参数的函数式接口
@FunctionalInterface
public interface ResponseOneArgInterface<T, Q> {
Q returnResponse(T t);
}
2、使用单参数函数式接口
@GetMapping("/order/delNoPayOrder")
@ResponseBody
@ApiOperation(value = "删除某一用户的未支付订单", notes = "需要携带token")
@ApiImplicitParam(name = "userId", value = "用户登录的微信openid前9位", required = true, dataTypeClass = String.class)
public GeneralResponse<Boolean> deleteNoPayOrder(String userId) {
ResponseOneArgInterface<String, GeneralResponse<Boolean>> responseOneArgInterface = (value) ->
orderService.deleteNoPayOrder(value)
? GeneralResponse.ServerSuccess(true, "管理员删除用户未支付订单成功")
: GeneralResponse.ServerError("管理员删除用户未支付订单失败:请检查数据库离线或查看日志排查");
return responseOneArgInterface.returnResponse(userId);
}
我们不难发现,这里笔者是使用函数式接口结合 (入参)-> { 根据函数式接口的返回确定返回 },
使用时就可以直接调用它的惟一的那个抽象方法即可获得处理结果,这里如果在前后进行别的操作(前提是不要留下异常隐患),也不影响我们最终的返回(函数式编程不会影响函数外部的状态和变量)。
3、定义双参数函数式接口
@FunctionalInterface
public interface ResponseTwoArgInterface<T, K, Q> {
Q returnResponse(T t, K k);
}
4、使用双参数函数式接口
@GetMapping("/updatePassword")
@ResponseBody
@ApiOperation(value = "修改管理员信息", notes = "需要携带token")
@ApiImplicitParams({
@ApiImplicitParam(name = "managerPhone", value = "管理员电话", required = true, dataTypeClass = String.class),
@ApiImplicitParam(name = "password", value = "新密码", required = true, dataTypeClass = String.class),
@ApiImplicitParam(name = "request", value = "请求对象", required = true, dataTypeClass = HttpServletRequest.class)})
public GeneralResponse<Manager> managerUpdatePassword(String managerPhone, String password, HttpServletRequest request) {
ResponseTwoArgInterface<String, String, GeneralResponse<Manager>> responseThreeArgInterface = (value1, value2) -> {
String token = request.getHeader("token");
Boolean updateManagerPassword = managerService.updateManagerPassword(value1, value2);
if (updateManagerPassword && jedisService.delOldToken(token)) {
Manager manager = managerService.managerLogin(value1, value2);
if (Objects.nonNull(manager)) {
jedisService.createToken(value1 + "&" + value2, MagicNumber.DEFAULT_OPTION_COUNT, MagicNumber.DEFAULT_TIME_OUT);
return GeneralResponse.ServerSuccess(manager, "管理员修改密码成功");
}
return GeneralResponse.ServerError("管理员修改密码成功,但由于某些原因数据库产生了异常,请查看日志排查");
} else {
return GeneralResponse.ServerError("管理员修改密码失败: 请检查旧的token密钥是否清空成功或查看日志排查");
}
};
return responseThreeArgInterface.returnResponse(managerPhone, password);
}
此时我们发现函数内部的逻辑变得复杂,但是这仍然和函数外部的逻辑无关,接口的返回只由函数内部的逻辑决定。
函数式接口的使用很简单,但是对于某些和笔者一样什么东西都喜欢用在架构上并观察对比思考 架构发生了什么变化,复杂度变化,耦合度变化,性能变化,优雅不优雅?
不断尝试,热爱思考,持续学习。