文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

这个类库可以帮助你理解Java中的函数式编程

2024-12-03 03:30

关注

每当JDK发布了新版本就有同学说“你发任你发,我用Java 8”,可在工作中有不少人依然不太擅长使用Java8的新特性,而这些特性往往让Java不再“臃肿”。不过我个人认为Java8所有的新特性中最具有代表性的一定是函数式编程。有人会说这种风格太抽象难懂了,当你熟练掌握这种设定之后,你一定会感到很香。慢慢地你也会领会到函数式编程的魅力和精髓。今天介绍一个函数式Java工具包,它表现了很多优秀的函数式编程思想。以前介绍的熔断降级组件Hystrix的替代品resilience4j就基于vavr库。

Vavr

Vavr是一个Java8函数库,它运用了大量的函数式编程范式。创造性地封装了一些持久性的数据结构和函数式控制结构。而且从中可以学到很多有用的编程思想。

可观察的副作用

我们的代码中经常会出现一些看不见的陷阱,从代码语义中这些陷阱是无法被观察的。例如

  1. int divide(int a, int b){ 
  2.  return a/b; 

我们知道a/b会得到一个整数,但是却不能从代码上明确地知道如果b=0将会抛出java.lang.ArithmeticException异常;而如果是a+b则不会带来任何副作用。所以我们需要让这种副作用是可观察的。对于这一点Vavr做出了一种设计:

  1. Try<Integer> divide(Integer a, Integer b) { 
  2.     return Try.of(() -> a / b); 

将可能的副作用封装到一个容器中,明确了可能的失败,当你看到返回的是Try时,就意味着结果可能“并不顺利”,以便于针对性地进行预防。

不可变的数据结构

很多语言都在使用不可变的数据结构,比如Golang、Kotlin。主要原因是不可变的值:

为此Vavr设计了一个集合类库,旨在代替Java中的集合框架。Vavr 的集合库包含一组丰富的函数式数据结构,这些数据结构建立在 lambdas 之上。它们与 Java 原始集合共享的唯一接口是Iterable。这些数据结构是持久性的,一旦初始化本身就不可改变,你可以使用一些操作来返回更改后的副本。例如经典的数据结构单向链表:

  1. // 1   2  3 
  2. List<Integer> source = List.of(1, 2, 3); 

如果我们将一个新元素0放在原始链表尾部的前面

  1. //  0  2  3 
  2. List<Integer> newHeadList = source.tail().prepend(0); 
  3. //  1  2  3 
  4. System.out.println(source); 

原始链表保持不变,新的链表大小保持不变元素被替换了。当然你可以使用其它API来生成一个大小变化的副本,不过可以肯定的是原始的链表一定不会发生改变。

  1. // 0 1 2 3 
  2. List<Integer> prepend = source.prepend(0); 
  3. // 1 2 3 0 
  4. List<Integer> append = source.append(0); 

这只是其中的一部分编程思想,接下来我将介绍Vavr的一些特色。

Vavr的一些特色

Vavr提供了一些非常有用的而且有特色的API。

元组

熟悉Python的同学对元组(Tuple)一定不陌生。元组将固定数量的元素组合在一起,以便它们可以作为一个整体传递。与数组或列表不同,元组可以包含不同类型的对象,但它也是不可变的。目前Vavr提供了最多8个元素的元组结构。

  1. // (felord.cn, 22) 
  2. Tuple2Integer> java8 = Tuple.of("felord.cn", 22);  
  3. // felord.cn 
  4. String s = java8._1;  
  5. // 22 
  6. Integer i = java8._2; 

这个可以用来模拟Java中不具有的多返回值的特性。

Function

Java本身提供了Function接口,但是Vavr则提供了更加丰富的Function扩展,例如可以组合多个Function

  1. Function1<IntegerInteger> multiplyByTwo = a -> a * 2; 
  2. Function1<IntegerInteger> compose = multiplyByTwo.compose(a -> a + 1); 
  3. // 6 
  4. Integer apply = compose.apply(2); 

除此之外,还可以让潜在的副作用降级(lift),有点类似于微服务的熔断,以避免在函数执行中处理异常

  1. Function2<IntegerIntegerInteger> divide = (a, b) -> a / b; 
  2. // 降级  
  3. Function2<IntegerIntegerOption<Integer>> safeDivide = Function2.lift(divide); 
  4. // 返回一个加强版的Optional 
  5. Option<Integer> apply = safeDivide.apply(1, 0); 
  6. boolean empty = apply.isEmpty(); 
  7. // true 
  8. System.out.println(empty); 

还有派生操作:

  1. Function2<IntegerIntegerInteger> divide = (a, b) -> a / b; 
  2. Function1<IntegerInteger> a = divide.apply(4); 
  3. Integer apply = a.apply(2); 

这有点类似于柯里化,当我们用到更多入参时柯里化才更加明显:

  1. Function3<IntegerIntegerIntegerIntegersum = (a, b, c) -> a + b + c; 
  2. final Function1<Integer, Function1<IntegerInteger>> add2 = sum.curried().apply(1); 
  3. Integer apply = add2.apply(2).apply(3); 

猜一猜答案是几?

带有特性的值容器

这个不太好用中文说明,有一些值带有独特的性质,比如开头提到的Try,用来显式表明可能遇到异常。Vavr提供了很多具有独特性质的值容器。

Option

类似Optional,但是比Optional更加强大。

Lazy

Lazy是一个惰性计算的容器,表示当使用时才去计算且只计算一次。

  1. Lazy<Double> lazy = Lazy.of(Math::random); 
  2. lazy.isEvaluated(); // = false 
  3. lazy.get();         // = 0.123   
  4. lazy.isEvaluated(); // = true 
  5. lazy.get();         // = 0.123  
  6. // 需要使用数据时才从数据源加载 
  7. Data lazyData = Lazy.val(DataSourceService::get, Data.class); 

其它还有一些非常有用的容器,你可以尝试它们。

模式匹配

函数式编程语言大都支持模式匹配,同为JVM语言的Scala中就有这种特性,而Java目前是没有的。可以有效地帮助我们减少if-else,举个例子:

  1. public static String convert(int input) { 
  2.  
  3.       String output
  4.       if (input == 1) { 
  5.           output = "one"
  6.       } else if (input == 2) { 
  7.           output = "two"
  8.       } else if (input == 3) { 
  9.           output = "three"
  10.       } else { 
  11.           output = "unknown"
  12.       } 
  13.       return output
  14.   } 

你就说吧,绕不绕?,Vavr就清爽多了。

  1. public static String vavrMatch(int input) { 
  2.      return Match(input).of
  3.              Case($(1), "one"), 
  4.              Case($(2), "two"), 
  5.              Case($(3), "three"), 
  6.              Case($(), "unknown"
  7.      ); 
  8.  } 

当然还有其它一些玩法需要你自己去发现。

总结

函数式编程作为Java8最大的一个亮点(个人认为),对于习惯于传统OOP编程的开发者来说确实不容易接受。你不妨从Vavr类库入手去学习函数式编程的思想。今天介绍的只是它很少的一部分,还有更多等着你去发现、去借鉴。忘记说了,如果你想在项目中引用它,可以引入下面这个坐标:

  1. -- https://mvnrepository.com/artifact/io.vavr/vavr --> 
  2.  
  3.     io.vavr 
  4.     vavr 
  5.     0.10.3 
  6.  

本文转载自微信公众号「 码农小胖哥」,可以通过以下二维码关注。转载本文请联系码农小胖哥公众号。

 

来源: 码农小胖哥内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯