当前位置: 首页 > 后端技术 > Java

Java中的函数式编程(一)

时间:2023-04-01 16:09:53 Java

的概念写的比较早。从Java8开始,Java语言增加了lambda表达式和函数式接口等新特性。这意味着Java语言也开始逐渐提供函数式编程的能力。其实,如果你熟悉Erlang、Scala、JavaScript或Python,那么你或多或少对函数式编程是比较熟悉的。但是如果你是一个通过常规路径学习的Javaer,你可能对函数式编程的思想了解不多。相对来说,你可能更熟悉面向对象的编程思想。先熟悉几个术语,有助于提高技能:FP,FunctionalProgramming,函数式编程只要。为什么FP会突然火起来?主要原因是摩尔定律正在逐渐失效,单核CPU的计算能力短期内无法取得重大突破。计算机领域正朝着“多核CPU、分布式计算”的方向发展。而FP,天然具备适合并发编程的优点:不修改变量,不存在多线程之间的竞争问题,所以不需要考虑“锁”和“线程阻塞”,并且很容易并发编程。在未来的大数据时代,函数式编程的思想会越来越重要。其次,在实际编码过程中,程序员发现即使在OOP的世界里,FP也能更好地解决一些特定的问题,是对OOP的有益补充。因此,出现了很多混合风格的代码(hybrid指的是OOP+FP),这也是为什么Java这种老牌的OOP编程语言引入了FP的新特性。本系列文章重点介绍Java函数式编程。Java是一种经典的OOP编程语言。在实际应用中,大多数Java程序都是OOP+FP的混合代码。因此,对于函数式编程中的一些高级特性和技巧,如柯里化、惰性求值、尾递归等,我们不会做特别的阐述。有兴趣的同学可以搜索公众号,工作人员说,一起交流。接下来,我们来看看函数式编程的定义和它的优点。本文的示例代码可以从gitee获取:https://gitee.com/cnmemset/ja...什么是函数式编程?函数式编程是一种编程范式,其追求的目标是整个程序由函数调用(functionapplying)和函数组合(functioncomposing)组成。函数调用大家很容易理解,但是在函数式编程中,函数调用有一个局限性——它不会改变函数外的其他状态,换句话说,函数调用不会改变定义在函数外的变量的值。这种函数有一个专门的名词——纯函数(purelyfunction)。纯函数有一个特征。在参数值不变的情况下,多次运行纯函数,结果始终相同。此功能对于纯函数的单元测试和调试特别有益。函数组合是指将一系列简单的函数组合起来形成一个复合函数。函数组合是一个比较复杂的概念,比如在Python中:fromfunctoolsimportreducedefcompose(*funcs)->int:"""将一组简单的函数[f,g,h]组合成一个复合函数(f(g(h(...))))"""returnreduce(lambdaf,g:lambdax:f(g(x)),funcs)例子f=lambdax:x+1g=lambdax:x*2h=lambdax:x-3调用复合函数f(g(h(x)):[(x-3)*2]+1print(compose(f,g,h)(10))//打印15in在Java中,java.util.Objects.Consumer接口的默认方法andThen是一个简单的函数组合函数:defaultConsumerandThen(Consumerafter){Objects.requireNonNull(after);return(Tt)->{accept(t);after.accept(t);};}函数式编程的特点函数式编程有几个重要的特点:1.函数是“一等公民”函数是“一等公民”,这意味着函数与其他数据类型具有相同的地位——它们可以分配给一个变量,可以用作另一个函数的参数,或者可以用作另一个函数的返回值。判断某种开发语言对函数式编程支持程度的重要标准是该语言是否将函数视为“一等公民”。例如,在下面的Java代码中,print变量可以看作是一个匿名函数,作为参数传入函数ArrayList.forEach中。更多语言细节可以参考后续系列文章。publicstaticvoidsimpleFunctinoProgramming(){Listl=Arrays.asList("a","b","c");Consumerprint=s->System.out.println(s);l。forEach(print);}上面的代码会输出:abc2。没有“sideeffects”和“sideeffects”,意思是函数执行时,除了获取计算结果外,还会改变函数的状态以外的东西。“副作用”的典型场景是修改程序的全局变量(如Java中某个全局可见类的属性值、某个类的静态变量等);修改传入参数也是“副作用”之一;有“副作用”的IO操作或调用其他函数也是“副作用”。在函数式编程中,要求函数是“纯函数(purelyfunction)”。给定参数,多次运行一个纯函数,总是得到相同的返回值,不会修改函数外的状态,也不会产生其他“副作用”。“副作用”的含义如此严格,但有时我们需要保存计算过程中的状态,却不能使用可变变量。这时候我们就用递归的方式,通过保存在栈上的参数来记录状态。下面的代码是一个经典的例子,它定义了一个函数reverse来反转一个字符串。可以看出,reverse在执行时的中间状态是在递归时由它的参数保存的。请记住,在函数式编程中,所有参数和变量都是最终的(只能赋值一次,赋值后不能更改):publicstaticStringreverse(finalStringarg){if(arg.length()==0){returnarg;}else{returnreverse(arg.substring(1))+arg.substring(0,1);}}我们可以使用递归来解决类似的问题,虽然它的性能并不讨人喜欢,但它确实非常适合函数式编程风格。至此,就有一个疑问:函数式编程有这么多的限制,性能似乎也不是很好。为什么我们还提倡函数式编程?原因是函数式编程有很多优势,尤其是在大数据&&多核计算时代。3.参照透明性(Referentialtransparency)是指函数的运行不依赖于外部状态或外部变量,而只依赖于输入参数。只要参数相同,运行函数的返回值总是相同的。在其他编程范例中,函数的返回值通常与系统的状态有关。在不同的状态下,返回值可能不同。这对于测试和调试程序是非常不利的。函数式编程的优点1.方便单元测试和调试(Debugging)。由于函数式编程“无副作用”和“引用透明”的特点,每个函数都是一个独立的逻辑单元,不依赖于外部状态或变量,也不修改外部状态或变量。参数给定后,多次运行该函数,总是得到相同的返回值。这是单元测试人员和调试人员的理想情况。2、易于“并发编程”同样,函数式编程不依赖也不修改外部状态或变量,所以我们不需要考虑多线程并发竞争、死锁等问题,因为我们不需要添加“完全”“锁”。这样一来,并发编程的复杂度会大大降低,部署起来也很方便。在大数据和多核时代,这个优势被大大放大,这是主要函数式编程思想之所以重新焕发活力3.方便热部署的优势也很明显——因为函数式编程不依赖也不修改外部状态或变量,所以只要接口不变,我们就可以随时升级代码结论需要牢记函数式编程(FP)是一种编程范式或编程思想,它的类比是面向对象编程(OOP)。OOP的本质在于“封装对象”,而FP的本质在于“不涉及外部状态”。一种开发语言可以同时支持OOP和FP,两者并不矛盾。一种开发语言,即使号称真正支持函数式编程,也不能100%符合函数式编程风格,因为IO操作有“副作用”,但实际上不做I/O是不可能的。因此,在实际应用中,函数式编程只需要将I/O的影响降到最低,而更侧重于保持计算过程的简单性。