当前位置: 首页 > 科技观察

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

时间:2023-03-21 17:42:45 科技观察

每当JDK出新版本的时候,有同学说“你随便贴吧,我用的是Java8”,但是很多人在工作中还是不明白.太善于利用Java8的新特性,而这些特性往往让Java不再“臃肿”。但是我个人认为Java8的所有新特性中最具代表性的一定是函数式编程了。有人会说,这种风格太抽象,难以理解。当你掌握了这个设定,你一定会觉得很香。慢慢的你也会领略到函数式编程的魅力和精髓。今天介绍一个函数式Java工具包,里面展示了很多优秀的函数式编程思想。Resilience4j是之前引入的fuse降级组件Hystrix的替代品,它基于vavr库。VavrVavr是一个Java8函数库,大量使用了函数式编程范式。创造性地封装了一些持久化数据结构和功能控制结构。而且你可以从中学到很多有用的编程思想。可观察的副作用我们的代码中往往存在一些看不见的陷阱,无法从代码的语义上观察到。比如intdivide(inta,intb){returna/b;}我们知道a/b会得到一个整数,但是从代码中我们不能清楚的知道如果b=0,会抛出java.lang.ArithmeticException;如果是a+b则不会带来任何副作用。所以我们需要让这种副作用可见。针对这一点,Vavr做了一个设计:Trydivide(Integera,Integerb){returnTry.of(()->a/b);}将可能的副作用封装到一个容器中,明确了可能的Failure,当你看到返回的是Try,说明结果可能“不顺利”,这样可以有针对性地预防。不可变数据结构不可变数据结构在许多语言中都有使用,例如Golang和Kotlin。主要原因是不可变值:本质上是线程安全的,所以不需要同步,对equals和hashCode可靠,不需要克隆,在uncheckedcasts中类型安全,函数式编程的不可变值为了最透明,Vavr设计一个用于此目的的集合类库,旨在取代Java中的集合框架。Vavr的集合库包含一组构建在lambda之上的丰富的功能数据结构。它们与Java原始集合共享的唯一接口是Iterable。这些数据结构是持久的,一旦初始化就不会改变,你可以使用一些操作来返回改变的副本。比如经典的数据结构单向链表://123Listsource=List.of(1,2,3);如果我们在原链表的尾部前面放一个新元素0//023ListnewHeadList=source.tail().prepend(0);//123System.out.println(source);原链表不变,新链表大小不变。元素被替换。当然,你可以使用其他API生成一个大小变化的副本,但是可以肯定的是,原来的链表是不会变化的。//0123Listprepend=source.prepend(0);//1230Listappend=source.append(0);这只是编程思想的一部分。接下来,我将介绍Vavr的一些特性。Vavr的一些特性Vavr提供了一些非常有用和有特色的API。元组熟悉Python的同学一定对元组不陌生。元组将固定数量的元素组合在一起,以便它们可以作为一个整体传递。与数组或列表不同,元组可以包含不同类型的对象,但它也是不可变的。目前Vavr提供了最多包含8个元素的元组结构。//(felord.cn,22)Tuple2java8=Tuple.of("felord.cn",22);//felord.cnStrings=java8._1;//22Integeri=java8._2;这个可以用来模拟Java中不存在的返回值多的特性。FunctionJava本身提供了Function接口,但是Vavr提供了更丰富的Function扩展,比如可以组合多个FunctionFunction1multiplyByTwo=a->a*2;Function1compose=multiplyByTwo.compose(a->a+1);//6Integerapply=compose.apply(2);另外,它还可以降低潜在的副作用(lift),有点类似于微服务的熔断器,避免在函数执行时处理ExceptionFunction2divide=(a,b)->a/b;//降级Function2>safeDivide=Function2.lift(divide);//返回一个提升版本的OptionalOptionapply=safeDivide.apply(1,0);booleanempty=apply.isEmpty();//trueSystem.out.println(empty);和派生操作:Function2divide=(a,b)->a/b;Function1a=divide.apply(4);Integerapply=a.apply(2);这有点像柯里化,当我们使用更多的时候柯里化在输入参数时更明显:Function3sum=(a,b,c)->a+b+c;finalFunction1>add2=sum.curried().apply(1);Integerapply=add2.apply(2).apply(3);猜猜答案是什么?有特点的值容器用中文不太好解释,有一些值具有特有的属性,比如开头提到的Try,用于明确表示可能遇到异常。Vavr提供了许多具有独特属性的价值容器。Option类似于Optional,但比Optional更强大。LazyLazy是惰性计算的容器,即用到时计算,只计算一次。Lazylazy=Lazy.of(Math::random);lazy.isEvaluated();//=falselazy.get();//=0.123lazy.isEvaluated();//=truelazy.get();//=0.123//需要数据时从数据源加载数据。lazyData=Lazy.val(DataSourceService::get,Data.class);还有一些其他非常有用的容器,你可以试试。模式匹配大多数函数式编程语言都支持模式匹配。同样是JVM语言的Scala有这个特性,而Java目前没有。可以有效的帮助我们减少if-else,例如:publicstaticStringconvert(input){Stringoutput;if(input==1){output="one";}elseif(input==2){output="two";}elseif(input==3){output="three";}else{output="unknown";}returnoutput;}告诉我,它绕不绕?Vavr更令人耳目一新。publicstaticStringvavrMatch(ininput){returnMatch(input).of(Case($(1),"one"),Case($(2),"two"),Case($(3),"three"),Case($(),"unknown"));}当然还有其他的玩法需要你自己去发现。总结函数式编程作为Java8最大的亮点之一(在我看来),对于习惯了传统OOP编程的开发者来说,着实不易。不妨从Vavr类库开始学习函数式编程的思想。今天的介绍只是其中的一小部分,还有更多等你去发现和学习。忘了说了,如果想在项目中引用,可以引入如下坐标:io.vavrvavr0.10.3本文转载自微信公众号“码农小胖哥”,可以关注通过以下二维码。转载本文请联系码农小胖公众号。