承接上文:Java函数式编程:一、函数式接口,lambda表达式和方法引用

这次来聊聊函数式编程中其他的几个比较重要的概念和技术,从而使得我们能更深刻的掌握Java中的函数式编程。

本篇博客主要聊聊以下几个问题

  • 高阶函数
  • 闭包概念
  • 函数组合处理函数的使用
  • 柯里化以及部分求值

    下面开始:

1、高阶函数

高阶函数这里指的可不是数学里的那个,这里主要要从维度这个概念理解,什么叫高维?如果a是一维,那么生成a的存在就是高维。在这里,函数就是这个a,高阶函数指的就是能生成函数或是以函数为参数的函数

定义:高阶函数是一个能接受函数作为参数能够把函数作为返回值的函数。

  1. public interface Function{
  2. String str(String s);
  3. }
  4. public class Procudure{
  5. // 下面就是一个标准的高阶函数
  6. public Function(String s){
  7. return s -> s.upperCase();
  8. }
  9. }

这里有两点:

  • 我们可以通过继承java.util.function中的接口,或是自定义一个函数式接口来为专门的接口创建别名
  • 有了lambda表达式,很明显,我们很轻松就能创建并返回一个函数

但是这只是基本的,还记得函数式编程的意义吗?这里的关键在于,有时候,我们可以根据接受的函数,让高阶函数生成一个新的函数。

  1. public class test {
  2. public static Function<String, String> transform(Function<String, String> f){
  3. return f.andThen(String::toUpperCase);
  4. }
  5. public static void main(String[] args) {
  6. Function<String, String> transform = test2.transform(str -> {
  7. return str.substring(0, 2);
  8. });
  9. String s = transform.apply("abcdefg");
  10. System.out.println(s);
  11. }
  12. }

可以看到,这里我们通过andThen()这个Function接口的通用方法,连接了前后两个方法,并且使得无论我们输入了什么,都会将该字符串转化为全大写,后面我们输入了一个截取前两个字符作为返回值的方法,但很明显,这里可以有更多的选择,并且我们实际上也可以通过方法引用来引用某些定义好的函数,非常灵活。


2、闭包

什么是闭包?

考虑一个lambda表达式,它使用了其函数作用域之外的变量。当返回该函数时会发生什么呢?也即,当我们通过调用lambda表达式产生的匿名方法引用这些外部变量会发生什么呢?

如果一门语言能够解决这个问题,我们就认为该语言是支持闭包的,或者也可以说它支持词法作用域。

这里还涉及到一个术语:变量捕获

上面听起来是不是不明白,没关系,给个例子:

  1. public class Example{
  2. IntSupplier plus(int x){
  3. int y = 1;
  4. return () -> x + y;
  5. }
  6. }

考虑这个类和其中的方法plus(int x),你会不会发现有一些问题。

因为我们的plus(int x)方法返回的是一个函数,这里假设返回的函数是f(int x),也就是说,f(int x)返回时,plus(int x)已经执行结束,所以其中的变量int y = 1;已经脱离了作用域,那么等到我们获取了f(int x)的对象再调用到f(int x)方法时,这个y要怎么办呢?

你会发现,上面的这个方法是可以被编译执行成功的,但是下面的这个就不行:

  1. public class Example{
  2. IntSupplier plus(int x){
  3. int y = 1;
  4. return () -> x + (++y);
  5. }
  6. }

为什么呢?编译器提示:lambda 表达式中使用的变量应为 final 或有效 final

这句话就说的很明白了,对于第一个例子,我们的y虽然没final关键字,但它是事实上的final变量,一旦这里赋值就不会再改动,而对于第二个方法来说则相当于把y赋予了新的值。

这里如果我们使用的是引用,比如下面这个例子

  1. public class Example{
  2. IntSupplier plus(int x){
  3. Queue<Integer> y = new LinkedList<>();
  4. y.offer(1);
  5. return () -> x + y.poll();
  6. }
  7. }

注意,这里是可以通过编译的,因为实际上我们只需要保证这个引用所指向的对象不被修改,避免后面调用返回的函数时却突然发现找不到对应的对象即可。

所以,Java提供的闭包的条件是,我们必须要能够保证,被捕获的变量是final

不过要注意的是,这里如果是多线程情况的话,不能保证线程安全。


3、函数组合

之前我们有提到andThen()这个方法,这些方法在Java.util.function包中的各个函数式接口中各有提供,总的来说有这么几种:

  • andThen(arg)

    • 先执行原始操作,再执行参数操作
  • compose(arg)
    • 先执行参数操作,再执行原始操作
  • and(arg)
    • 对原始谓词和参数谓词做逻辑与运算
  • or(arg)
    • 对原始谓词和参数谓词做逻辑或运算
  • negate()
    • 所得谓词为原始谓词取反

在后面的流处理章节,你将会体会到这些函数组合的力量。


4、柯里化和部分求值

所谓柯里化,就是指将一个接受多个参数的函数转变为一系列,只接受一个参数的函数,在面向函数编程里这么做的目的,就跟我们在面向对象编程里需要抽象出接口和抽象类是一样的,目的就是我们可以通过部分求值来复用这些代码。

  1. public class Currying{
  2. // 未柯里化的函数
  3. static String unCurried(String a,String b){
  4. return a + b;
  5. }
  6. // 柯里化的函数
  7. static Function<String, Function<String, String>> Curried(){
  8. return a -> b -> a + b;
  9. }
  10. // 实例
  11. public static void main(String[] args) {
  12. Function<String, Function<String, String>> curried = test2.Curried();
  13. System.out.println(unCurried("hello ", "World"));
  14. Function<String, String> firstWord = curried.apply("hello ");
  15. System.out.println(firstWord.apply("World"));
  16. System.out.println(firstWord.apply("My friend"));
  17. System.out.println(firstWord.apply("My love"));
  18. }
  19. /**
  20. 输出
  21. hello World
  22. hello World
  23. hello My friend
  24. hello My love
  25. **/
  26. }

简单来说就是每一层都返回下一层的函数,直到最终返回我们需要的值为止。

Java函数式编程:二、高阶函数,闭包,函数组合以及柯里化的更多相关文章

  1. python 函数式编程:高阶函数,map/reduce

    python 函数式编程:高阶函数,map/reduce #函数式编程 #函数式编程一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数 #(一)高阶函数 f=abs f print ...

  2. Learning Python 012 函数式编程 1 高阶函数

    Python 函数式编程 1 高阶函数 高阶函数 Q:什么是高阶函数? A:一个函数接收另一个函数作为参数,这种函数就称之为高阶函数. 简单举个例子: def add(x, y, f): return ...

  3. Scala - 快速学习08 - 函数式编程:高阶函数

    函数式编程的崛起 函数式编程中的“值不可变性”避免了对公共的可变状态进行同步访问控制的复杂问题,能够较好满足分布式并行编程的需求,适应大数据时代的到来. 函数是第一等公民 可以作为实参传递给另外一个函 ...

  4. swift 学习(二)基础知识 (函数,闭包,ARC,柯里化,反射)

    函数 func x(a:Int, b:Int)  {}   func x(a:Int, b:Int) -> Void {}  func x(a:Int, b:Int) ->(Int,Int ...

  5. 从bind函数看js中的柯里化

    以下是百度百科对柯里化函数的解释:柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术.概念太抽象,可能 ...

  6. python_08 函数式编程、高阶函数、map、filter、reduce函数、内置函数

    函数式编程 编程方法论: 1.面向过程 找到解决问题的入口,按照一个固定的流程去模拟解决问题的流程 (1).搜索目标,用户输入(配偶要求),按照要求到数据结构内检索合适的任务 (2)表白,表白成功进入 ...

  7. Python3基础(3)集合、文件操作、字符转编码、函数、全局/局部变量、递归、函数式编程、高阶函数

    ---------------个人学习笔记--------------- ----------------本文作者吴疆-------------- ------点击此处链接至博客园原文------ 1 ...

  8. 小白的Python之路 day3 函数式编程,高阶函数

    函数式编程介绍   函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计.函数就是面向过程的 ...

  9. Python3学习之路~3.2 递归、函数式编程、高阶函数、匿名函数、嵌套函数

    1 递归 在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数. def calc(n): print(n) if int(n / 2) == 0: return n r ...

随机推荐

  1. scratch制作彩虹猫病毒模拟器

    scratch制作彩虹猫病毒模拟器 hello,大家好. 编程慢慢更加接近生活,甚至小孩也开始学了,比如scratch编程,小编今天就带了一件作品(彩虹猫病毒模拟器) 我们先看一下效果| 做的还可以, ...

  2. Queue-jumpers - 平衡树

    题面 Ponyo and Garfield are waiting outside the box-office for their favorite movie. Because queuing i ...

  3. Html飞机大战(四):状态的切换(界面加载类的编辑)

    好家伙,接着写   既然我们涉及到状态了,那么我们也会涉及到状态的切换   那么我们怎样切换状态呢? 想象一下,如果我玩的游戏暂停了,那么我们肯定是通过点击或者按下某个按键来让游戏继续   这里我们选 ...

  4. CAD问题

    经过长时间的摸索,终于弄好了du.我是通过禁用硬件加速可zhi以用的.方法就是打开cad,点击左上角dao的那个黑色小三角,再点击"选项","系统"," ...

  5. Gitea 1.17.2 | 带来视觉提升、完善资源校验、加强安全性等42项优化

    Gitea 1.17.2 合并了 42 个 Pull Request,现已正式发布,我们建议所有用户升级到此版本.您可以到阅读原文了解更详细的介绍. 致谢:@zeripath 为 Gitea 贡献了诸 ...

  6. 学习ASP.NET Core Blazor编程系列二——第一个Blazor应用程序(完)

    学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...

  7. 02-MyBatisPlus入门

    快速开始参考:https://baomidou.com/pages/226c21/ 测试项目: mybatis_plus 数据库:mybatis_plus 一.创建并初始化数据库 1.创建数据库: m ...

  8. 天天写SQL,这些神奇的特性你知道吗?

    摘要:不要歪了,我这里说特性它不是 bug,而是故意设计的机制或语法,你有可能天天写语句或许还没发现原来还能这样用,没关系我们一起学下涨姿势. 本文分享自华为云社区<[云驻共创]天天写 SQL, ...

  9. HTTP和Servlet快速入门

    目录 1.HTTP 1.1 请求数据格式 1.2 相应数据格式 2.Servlet 3.Servlert的xml配置 1.HTTP 1.1 请求数据格式 请求行:请求数据的第一行 包含三个内容,按顺序 ...

  10. Java SE 多态

    1.多态 方法的多态 //方法重载体现多态 A a = new A(); //这里我们传入不同的参数,就会调用不同sum方法 System.out.println(a.sum(10,20)); Sys ...