java函数式编程_Java函数式编程三分钟入门

(全文阅读时间大概3分钟)JDK8通过java.util.function包提供了函数式编程(FP,functionalprogramming),Oracle此举把FP融入了Java这样一种命令式编程语言(imperativeprogramming)中。虽然目前Java中的FP不是纯粹的FP(pureFP),但它为Java注入了新的编程风格,新的编程思路,新的编程框架。

所谓命令式编程语言包括过程式编程语言(Procedure programming)和面向对象编程语言(Object oriented programming),它们关注如何解决问题,而FP关注如何描述问题,问题描述清楚后,问题也就解决了。使用命令式编程语言,程序的逻辑由一条条语句,一个个方法,对象以及它们之间的调用来完成,程序流程分支庞杂,测试困难,分支覆盖不全,很容易就导致程序崩溃,异常如果处理不当,也会埋藏隐患。

如果说软件正在“吞噬”着世界,那么函数则正在支配着软件。函数式编程正是函数支配软件的一个体现,它的基本思想利用函数来解决程序逻辑问题,和数学中的函数一样,相同的输入必定对应相同的输出,即函数是无状态的。其数学基础包括 演算(lambda calculus)和范畴论(category theory)等。

归纳起来,FP有以下特点:

1) 函数是“一等公民”(first class citizens)。OOP关注对象方法的调用,而FP关注函数之间的调用,因此习惯OOP的开发者需要改变思维来习惯FP的思想。

2) 状态不变(immutable state)。纯粹的函数都是无状态的,一系列的函数调用即transformation并不会改变初始对象的状态,只是程序运行的状态。

3) 支持高阶函数(higher-order functions):即一个或多个函数作为函数参数传入,或函数的返回结果是一个函数,满足其中一个条件即为高阶函数。

4) 无副作用(no side effects)。纯粹的函数都是无副作用的,即不依赖global变量,不会修改对象状态。

5) 支持函数组合(function composition):需要关注函数链条的执行顺序。

6) 惰性求值(Lazy evaluation):指一系列嵌套的函数的执行被延迟到其值需要的时刻。

下面看下JDK的具体实现。JDK8引入的FP接口主要放在了java.util.function包,我们选取有代表性的Function接口介绍。全文对OOP中的方法和FP中的函数不做过多区分,默认读者通过上述FP的特点能够领会。

  • Function接口:

6c89eb63b33f705552a157782184e3d5.png
  • FuntionalInterface注解表明其是一个函数接口,事实上任意一个接口如果只有一个抽象方法,都可作为函数接口使用,不过覆盖Object类的public方法的接口除外。函数R apply(T t)方法表明Function接收一个T类型的入参,返回一个R类型的对象。可以通过以下方式声明一个Function实例:
  • 方法一,通过lambda表达式:

d09704e0bacb349b3f08f973a60e1f98.png
  • 方法二:通过类引用(这里CLassName代表了multMethod所在的类名):

ce077ed192223c165df8dee702e3b0db.png
  • 方法三:通过匿名类返回:

c3f350d07d3a7651cb189c0dd624704c.png
  • Function中的函数组合:Function接口中包括两个default方法,

0032c3c11b5f162372889b989470af8c.png
  • compose组合函数先执行before函数,再执行当前函数;相反andThen先执行当前函数,再执行after函数。例如

33e0e0299211d59962ae73214fcd0c8d.png
  • 内层函数innerFun输出类型为Long,外层函数输入类型为Number,也就是说内层函数的输出类型必须为外层函数输入类型的子类,compose的方法签名中before函数的返回值范型为<? extends T>也说明这一点(范型中extends代表?(wildcard)继承自T,即?的上界(upper bounded)是T),before函数的入参<? super V>,V默认为Object类型(即?的下界(lower bounded)是V),因此入参可以是任意类型。参见范型相关介绍。
  • Consumer接口代表只有一个入参,没有返回的函数。抽象方法:void accept(T t); 表明代码中任何只有一个入参,没有返回的方法都可作为Consumer函数。函数组合方法:

25cf505cdfee56a056e75444ef5ba63c.png
  • 该方法表明Consumer函数链消费相同的入参,后续函数的入参类型为前序函数入参的父类,即函数链越往后,入参类型越抽象。例如:

859f355ebd8fa35beee1e2805458b7a1.png
  • Supplier接口只有一个 T get()方法,该接口代表只有一个类型为T的返回值,没有入参,代码中任何只有一个返回值,没有入参的方法都可作为一个Supplier函数。
  • UnaryOperator接口继承自Funtion接口,接口定义:public interface UnaryOperator<T>extends Function<T, T>,代表一元运算函数,即返回值与入参的类型一致,这个是与Funtion接口的区别。UnaryOperator与Funtion中都有一个静态identity方法,两者效果一样,返回值即为入参,所不同的就是UnaryOperator返回一个UnaryOperator,而Funtion中的identity返回一个Funtion。
  • Predicate接口代表逻辑判断函数。抽象方法boolean test(Tt)说明根据入参t,判断其是否满足一定的逻辑条件,返回bool结果。三个default方法:

1353442b93180fc8ee745b7a3c33d911.png
  • 分别代表了逻辑与,逻辑非,逻辑或。isEqual用于判断两个对象是否相 等的Predicate函数。

745fa5c8811580969a1d02b46ac373d2.png

另外,java.util.function下Bi*接口代表了双元函数,即输入参数为两个;其他包含有具体类型的接口,如:Long*,ToDouble*,LongBinaryOperator等适用于特定的类型,在熟悉了上述Function,Condumer,UnaryOperator,Predicate等接口后,就非常容易理解和运用它们了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值