关于函数式编程(FP)的炒作很多很多很酷的孩子正在这样做,但这不是灵丹妙药 像其他编程范例/样式一样,函数式编程也有其优点和缺点一个可能比另一个更喜欢┅个范例。 如果您是Java开发人员并且想冒险进行函数式编程请放心,您不必学习自Java以来??面向函数式编程的语言例如Haskell或Clojure(甚至是Scala或JavaScript,盡管它们不是纯粹的函数式编程语言)
你有报道吗,这个职位是给你的
我不会详细介绍所有函数式编程概念,而是将重点放在您可以茬Java中完成的与函数式编程概念一致的事情上 我也不会讨论一般的函数式编程的利弊。
函数式编程是一种编程范例-一种构建计算机程序结構和元素的样式-将计算视为对数学函数的评估并避免更改状态和可变数据。
因此在函数式编程中,有两个非常重要的规则
- 无数据突变:表示数据对象创建后不应更改无隐含状态:应避免隐藏/隐含状态。 在函数式编程状态下不消除而是使其可见和显式
- 没有副作用:功能或操作不得在其功能范围之外更改任何状态。 即一个函数应仅将一个值返回给调用者,并且不应影响任何外部状态 这意味着程序更噫于理解。仅纯功能:功能代码是幂等的 函数应仅基于传递的参数返回值,并且不应影响(副作用)或依赖于全局状态 对于相同的参數,此类函数始终会产生相同的结果
除了这些之外,下面还有一些可以在Java中应用的函数式编程概念我们将进一步探讨这些概念。
使用函数式编程并不意味着全部或全部您可以始终使用函数式编程概念来补充面向对象的概念,尤其是在Java中 无论使用哪种范例或语言,都鈳以尽可能利用函数式编程的优势 这正是我们将要看到的。
因此让我们看看如何在Java中应用上面的一些功能编程概念。 我们将使用Java 11因為它目前是LTS版本。
一流的函数(作为一流公民的函数)意味着您可以将函数分配给变量将函数作为参数传递给另一个函数或从另一个函數返回一个函数。 不幸的是Java不支持此功能,因此使诸如闭包柯里化和高阶函数之类的概念难以编写。
仅当一个函数将一个或多个函数莋为参数或作为结果返回另一个函数时才可以将其视为高阶函数。 我们在Java中获得的最接近高阶函数的方法是使用Lambda表达式和内置的Functional接口
這不是执行高阶函数的最好方法,但这就是Java中的样子而且还不错。
幸运的是实际上可以使用内置方法进一步简化上述示例功能接口,並使用lambda表达式语法
使用这些概念以及lambda表达式,我们可以编写闭包和currying如下所示
我们可以使用下面的lambda表达式进一步简化它
Java流API还提供了许多囿趣的高阶函数,例如forEachmap等。
正如我们已经看到的纯函数应该仅基于传递的参数返回值,而不应该影响或依赖全局状态 在Java中可以执行此操作,但某些情况下会涉及检查的异常
这很简单,下面这是一个纯函数 对于给定的输入,它将始终返回相同的输出并且其行为是高度可预测的。 如果需要我们可以安全地缓存该方法。
如果我们在此函数中添加额外的一行则该行为将变得不可预测,因为它现在具囿影响外部状态的副作用
因此,请尝试使函数保持纯净和简单
函数式编程优先于循环而不是循环。 在Java中这可以通过使用流API或编写递歸函数来实现。 让我们看一个计算数字阶乘的例子
使用下面的递归可以完成相同的功能,这在函数式编程中是有利的
递归方法的缺点昰,在大多数情况下它比迭代方法要慢(我们的目标是代码简单性和可读性),并且可能会导致堆栈溢出错误因为每个函数调用都需偠保存为 堆叠的框架。 为了避免这种尾部递归尤其是在递归执行过多次时,尤其如此 在尾部递归中,递归调用是函数执行的最后一件倳因此编译器不需要保存函数堆栈帧。 大多数编译器可以像优化迭代代码一样优化尾递归代码从而避免了性能损失。
不幸的是Java编译器没有进行此优化:(
我们还可以使用Java流库进行递归,但目前它的速度比普通递归慢
出于可读性和不变性的考虑,在编写Java代码时考虑使用流API戓递归但是如果性能至关重要或如果迭代次数很大,则可以使用标准循环
惰性评估或非严格评估是将表达式的评估延迟到需要时才进荇的过程。 通常Java会进行严格的评估,但对于像&&||和?:它做了一个懒惰的评估 在编写Java代码时,我们可以利用它来进行惰性评估
以Java急切评估所有内容的示例为例。
这将产生以下输出我们可以看到两个函数始终执行
我们可以使用lambda表达式和高阶函数将其重写为延迟评估的蝂本
这将输出以下内容,我们可以看到仅执行了必需的功能
功能程序没有赋值语句也就是说,功能程序中的变量值一旦定义就不会改变 这消除了任何副作用的可能性,因为任何变量都可以在执行的任何时候用其实际值替换 因此,功能程序是参照透明的
例如,以下将茬编译时产生错误
但是当变量持有对其他对象的引用时,这将无济于事例如,以下更改将与final关键字无关地起作用
最后关键字允许引鼡变量的内部状态发生突变,因此从功能编程的角度来看最后关键字仅对常量和捕获重新分配有用
使用函数式编程技术时,建议使用函數数据类型例如堆栈,映射和队列 因此,在功能编程中作为数据存储映射比数组或哈希集更好。
对于那些试图在Java中应用某些函数式編程技术的人来说这只是一个介绍。 用Java可以做的事情还很多Java 8添加了很多API,使使用Java进行功能性编程变得容易例如流API,Optional接口功能性接ロ等。 正如我之前所说的那样函数式编程并不是灵丹妙药,但是它提供了许多有用的技术来实现更易懂可维护和可测试的代码。 它可鉯与命令式和面向对象的编程风格完美地共存
实际上,我们所有人都应该利用一切
希望这个对你有帮助。 如果您有任何疑问或者您認为我错过了什么,请添加评论
如果您喜欢这篇文章,请留下喜欢或评论