经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Java » 查看文章
Java8函数式编程应用
来源:cnblogs  作者:红文  时间:2023/12/22 16:21:39  对本文有异议

我们经常提到,Java8是革命性的一个版本,原因就是正式引入了函数式编程,那Java的函数式编程在实际应用中到底有什么用呢?结合实际的应用,我整理出了函数式在Java的几个经典用途。

 

 

缓求值

惰性求值(Lazy evaluation)是在需要时才进行求值的计算方式。惰性求值自然地在数据结构中包含递归,可以以简单的方式表示无限的概念,这种方式有利于程序的模块化。

在Java这种Strict语言中,我们定义了临时变量代码块就会立即运算并且取得结果,而实际上很多结果未必会使用到,就存在不必要的计算,如下面的代码

  1. /**
  2. * 是否是标准或默认工作台
  3. * 理论上仅判断默认工作台即可(因为标准就是默认),此处是兜底一下历史逻辑
  4. *
  5. * @param context
  6. * @return
  7. */
  8. public boolean isStandardOrDefaultWorkbench(SchemaContext context) {
  9. Supplier<Boolean> isDefaultWorkbench = () -> StringUtils.isNotBlank(context.getDefaultAppUuid())
  10. && StringUtils.equals(context.getAppUuid(), context.getDefaultAppUuid());
  11. return WorkbenchType.WORKBENCH.equals(context.getWorkbenchType()) || isDefaultWorkbench.get();
  12. }

当我们使用临时变量定义的时候,需要理解计算出代码

  1. StringUtils.isNotBlank(context.getDefaultAppUuid())
  2. && StringUtils.equals(context.getAppUuid(), context.getDefaultAppUuid())

的值,并用于后面的判断,不管下面的代码是否为True,我们都消耗了一次计算

  1. WorkbenchType.WORKBENCH.equals(context.getWorkbenchType())

而我们使用了Supplier,就可以定义一个匿名表达式,只有当前面判断为False的时候,才会执行

  1. isDefaultWorkbench.get()

,而当前面判断为True的时候,就不会执行后面的代码了,通过缓求值的方式,可以节省在某些情况下的消耗,可以提升系统性能,节省资源。

 

高阶函数

高阶函数是指使用其他函数作为参数、或者返回一个函数作为结果的函数。

我们常用的Stream就是典型的流处理思想,可以通过Stream来处理集合并执行相关的操作,其中Stream包含了大量的高阶函数,我们可以直接使用匿名表达式来传递业务逻辑。值得注意,匿名表达式本身返回的就是函数,因此匿名表达式就是一种高阶函数。

  1. // 过滤隐藏应用
  2. return appDetails.stream().filter(app -> {
  3. AppModel appModel = new AppModel(app.getAppId(), app.getAppType());
  4. return !appDisplayModel.getHideApps().contains(appModel);
  5. }).collect(Collectors.toList());

 

从上面可以逻辑可以看到,我们可以通过集合的Stream对象进行filter、map、collect操作,其中这些高阶函数就可以传递lambda表达式,用于处理我们需要的逻辑。

当然除了Stream之外,Java很多工具类也支持外部传入lambda,比如还有一种常见的工具类Optional。

  1. /**
  2. * 获取组织默认工作台appUuid
  3. * @param corpId
  4. * @param orgConfigTag
  5. * @return
  6. */
  7. public String getDefaultWorkbenchAppUuid(String corpId, OrgConfigTag orgConfigTag) {
  8. return Optional.ofNullable(orgConfigTag).map(OrgConfigTag::getDefaultAppUuid)
  9. .orElseGet(() -> OpenPageUtils.buildWorkbenchAppUuid(corpId));
  10. }

 

其中的orElseGet方法就支持外部传入lambda表达式。

 

函数回调

在计算机程序设计中,回调函数,或简称回调(Callback 即call then back 被主函数调用运算后会返回主函数),是指通过参数将函数传递到其它代码的,某一块可执行代码的引用。 这一设计允许了底层代码调用在高层定义的子程序。

函数回调可用于接口两块完全无关的逻辑,比如在A模块执行完毕后,执行B模块,B模块的代码事先在A模块注册。很多框架型的逻辑,比如统一结果处理、缓存框架、分布式锁、线程缓存等都可以通过这个方式来优化,就可以使用lambda的方式来实现。核心的逻辑就是先处理相关的通用的中间件逻辑,然后通过lambda来执行业务逻辑。

  1. /**
  2. * 创建我的页面
  3. *
  4. * @param corpId
  5. * @param uid
  6. */
  7. protected void createMyPage(String corpId, Long uid, String appUuid, Method method) throws WorkbenchException {
  8. // 并发锁控制
  9. distributeLockSupport.lockAndDo(key,
  10. () -> {
  11. //创建页面方法
  12. userCorpPageSupport.createMyPage(corpId, uid);
  13. return null;
  14. }
  15. );
  16. }

 

如上面的代码,对于要实现分布式锁控制的模块,可以使用lambda的回调,来实现加锁和业务逻辑的分离。其中的定义如下述代码所示

  1. /**
  2. * 基于缓存的分布式锁操作
  3. *
  4. * @param key 资源key
  5. * @param callback 回调函数
  6. * @param <T> 返回结果类型
  7. * @return
  8. * @throws Exception
  9. */
  10. public <T> T lockAndDo(String key, Callback<T> callback) throws WorkbenchException {
  11. //取锁
  12. ...
  13. try {
  14. //加锁
  15. ...
  16. return callback.process();
  17. } catch (WorkbenchException ex) {
  18. throw ex;
  19. } finally {
  20. try {
  21. //释放锁
  22. ...
  23. } catch (Exception ex) {
  24. //异常处理
  25. }
  26. }
  27. }
  28. //回调接口定义,供外部传入lambda
  29. @FunctionalInterface
  30. public interface Callback<T> {
  31. /**
  32. * 回调处理
  33. *
  34. * @return 处理结果
  35. * @throws WorkbenchException
  36. */
  37. T process() throws WorkbenchException, ServiceException;
  38. }

 

 

自定义函数

从函数式编程的核心思想来说,函数是一等公民,而面向对象的核心是封装。可以通过定义函数的方式来降低面向对象命令式编程的复杂度。即尽量把处理逻辑都抽象成可复用的函数,并通过lambda的方式进行调用。

  1. /**
  2. * 批量操作
  3. *
  4. * @param consumer 函数式写法,对批量对象操作
  5. */
  6. public void batchProccess(BiConsumer<Long, OrchardDTO> consumer) {
  7. if(orchardDTOMap!=null && orchardDTOMap.size()>0){
  8. for (Map.Entry<Long, OrchardDTO> entry : orchardDTOMap.entrySet()) {
  9. consumer.accept(entry.getKey(), entry.getValue());
  10. }
  11. }
  12. }

 

  1. public void syncPush(MicroAppContext context, BatchOrchardDTO batchOrchardDTO) {
  2. //传入lambda来执行逻辑
  3. batchOrchardDTO.batchProccess((k, v) -> pushFacadeService.push(k, v));
  4. }

 

如上图所示,就是在类中定义了一个高阶函数,在调用的时候从外部传入lambda表达式,从而实现batchProccess和pushFacadeService.push两个方法的解耦。上面的核心和集合支持lambda是一个意思,也就是把需要外部频繁操作的部分抽象出来定义成函数式接口,以供外部传入不同的lambda进行丰富的操作。

 

总结

Java8非常重要的就是引入了函数式编程的思想,使得这门经典的面向对象语言有了函数式的编程方式。弥补了很大程度上的不足,函数式思想在处理复杂问题上有着更为令人称赞的特性。

原文链接:https://www.cnblogs.com/nengwenhuiwu/p/17920854.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号