本文继续上一篇的内容

在Java中,函数可以表现为一个普通的方法、一个lambda表达式,又或者方法引用,甚至是匿名类。本文不会介绍匿名类这种形式。

方法

Java中的方法,Java使用方法这一概念来表示我们称之为函数的东西。一个方法是函数式的,只要它满足纯函数的额要求(注:上一章有详细介绍)。但是在Java中,一个方法不可能被当成参数传递给另一个方法,你也就无法复合这些方法。Java的方法只属于它在的类。

函数的复合:

就和高中数学所学的那样,程序里的函数也可以相互复合。这里有两个函数:

f (x) = x+1;

g(x) = x*2;

函数f和g复合后即为f(g(x)) = f(x*2)=(x*2)+1;我们可以为新函数起个名,即h(x)= (x*2)+1;

将参数3应用于这个复合函数可得 f(g(3)) =6+1  =7;

所幸的是,Java为我们提供了函数式接口和lambda表达式来模拟这一过程。使用我们上一章定义的Function接口,我们可以表示上面的例子:

Function f = i -> i + 1;

Function g = i -> i * 2;

Function h = f.compose(g);

int result = h.apply(3); //“7”

我们也可以使用andThen方法,f.andThen(g)代表的含义是将f函数得到的结果作为参数应用到函数g。

这两个方法定义如下:

public interface Function {

U apply(T t);

default Function compose(Function f) {

return x -> apply(f.apply(x));

}

default Function andThen(Function f) {

return x -> f.apply(apply(x));

}

}

这是一个函数式接口,因为它只有一个抽象方法,接口中存在的默认方法不会影响到它的定义。

区别如图所示:

函数复合

我们可以看出这两个方法存在冗余,因为g.compose(f)与f.andThen(g)完全等价。

注:在使用Java官方提供的函数式接口时,你会遇到名为@FunctionalInterface的注解,

@FunctionalInterface

public interface Function {

……//仅含有一个抽象方法

}

这个标识性注解告诉编译器,这个类将被设计成函数式接口,有且仅有一个抽象方法。如果你用了这个注解但接口却定义了多个抽象方法

@FunctionalInterface

public interface Function {

U apply(T t);

void f();

}

这时编译器会提示你:

Invalid '@FunctionalInterface' annotation; Function is not a functional interface。

但这个注解不是必须的,你也可以不使用它,只要你的接口符合函数式接口的规范。然而,在编写函数式接口时使用它仍是一个好习惯。

方法引用

如果你觉得lambda难懂或麻烦,在某些情况下,可以用方法引用这种快捷写法代替。

请看下面的例子:

实例方法引用和类型的实例方法引用可能有点难以理解,实例方法引用,就是调用已经存在的实例的方法,这个对象实例是lambda表达式外部的实例。而类型的实例方法要求Lambda 参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数。

而对于构造函数的引用,根据构造函数参数的不同,可分为:

假设这有个类User{

……//

}

无参

Supplier supplier= User::new;

User user = supplier.get();

一个参数(假设参数类型为T):

Function function = User::new;

User user = function.apply(t);

两个参数(假设类型分别为T,U):

我们定义一个函数式接口

interface BiFunction(T,U,R){

R apply(T t,U u);

}

BiFunction  biFunction = User::new;

User user = biFunction.app(t,u);

其他的以此类推。我们需要有与构造方法匹配的函数式接口。

高阶函数及相关特性

如果一个函数(Java里称作方法)有多个参数,我们能永远将它看作只有一个参数的函数,而我们所做的,就是使用元组。函数有两个参数,可以说它有一个二元组参数,三个参数、四个参数亦是如此。例如,在scala里,我们可以简单定义一个元组,val t3= (1,2.0,”3”)  。它可以通过t3._1访问第一个元素,t3._2访问第二个元素,以此类推。

遗憾的是,Java并不支持元组类型,好在我们可以用类和泛型自定义自己的元组使用。例如二元组Tuple2、三元组Tuple3等。

下面是一个元组定义的例子,这是一个二元元组。

public class Tuple {

public final T _1;

public final U _2;

public Tuple(T _1, U _2) {

super();

this._1 = _1;

this._2 = _2;

}

@Override

public String toString() {

return "Tuple = [" + _1 + "][" + _2 + "]";

}

}

柯里化

在上一章中,我曾介绍过一个函数:

public static Function f(String args,Function> g){

return g.apply(args);

}

我说过,这和函数的柯里化有关,接下来我将想你介绍何为柯里化和部分施用。这两个概念极易混肴。某种意义上说,他们产生的效果,得到的结果是一样的,以至于在Groovy语言里,把他们统称为了柯里化。

简单来说,柯里化就是把一个多参函数转化为一串串单参函数的过程,参数可以一个接一个应用,除了最后一个参数外,其他参数都会产生一个全新的函数。

例如对函数F(X,Y,Z),柯里化后将会得到F(X)(Y)(Z)。你可决定到底对多少个参数实施变换。而部分使用就是提前带入一部分具体的参数值,部分求解后得到一个参数较少的函数。

例如对函数F(X,Y,Z),我们提前施用X的值,将会得到F(Y,Z)。

对于函数Function>来说,它会接受一个String类型的参数,并返回一个String->Integer类型的参数 。

例如:

f("abs",s->x->s.length()+x.length()).apply("abcds")

这个函数执行的结果为8,它只是将两个字符串的长度相加而已。实际上具体多少参数不做限制,只是Java里写起函数类型太麻烦。在Scala里,你可以这样表示:

f (“abs”) (“abcds”)。

对于上面这种可以接受函数作为参数,也可以将函数作为结果返回的函数,我们称之为高阶函数。

下一篇中,我将通过定义一个列表List,这个列表更加符合函数式的数据结构。同时通过这个列表向大家介绍函数式编程的三板斧,filter,map,fold.即过滤,映射,折叠(部分语言称为reduce归约)。

java 柯里化_函数式编程(Java描述)——Java中的函数及其柯里化相关推荐

  1. java编译器jdk版本_以编程方式确定Java类的JDK编译版本

    java编译器jdk版本 当需要确定使用哪个JDK版本来编译特定的Java .class文件时, 通常使用的方法是使用javap并在javap输出中查找列出的"主要版本". 我在我 ...

  2. Java 8 Lambda表达式的函数式编程– Monads

    什么是monad ?: monad是一种设计模式概念,用于大多数功能编程语言(如Lisp)或现代世界的Clojure或Scala中. (实际上,我会从scala复制一些内容.)现在,为什么它在Java ...

  3. 响应式编程 函数式编程_函数式编程简介

    响应式编程 函数式编程 根据您要求的对象, 函数式编程 (FP)是一种应运而生的开明编程方法,或者是一种在实践中几乎没有实际好处的过于学术化的方法. 在本文中,我将解释什么是函数式编程,探讨其好处,并 ...

  4. java 函数式编程_函数式编程杂谈

    比起命令式编程,函数式编程更加强调程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断演进,逐层推导出复杂的运算.本文通过函数式编程的一些趣味用法来阐述学习函数式编程的奇妙之处. 一 ...

  5. Java三行情书_函数式编程思维在三行代码情书中的应用

    函数式编程概述 如今主流的编程语言,函数式编程范式或多或少都融入其中成了"标配",或者说主流语言都在进行函数式方面的扩充,这是一个大趋势.以Java为例,随着 Lambda块 和 ...

  6. 面向java开发者的函数式编程_函数式编程让你忘记设计模式

    本文是一篇<Java 8实战>的阅读笔记,阅读大约需要5分钟. 有点标题党,但是这确实是我最近使用Lambda表达式的感受.设计模式是过去的一些好的经验和套路的总结,但是好的语言特性可以让 ...

  7. 项目中使用 java函数式编程_函数式编程在Java8中使用Lambda表达式进行开发

    事情起因 如果不喜欢看故事的同学,请前往下一章节. 事情起因是我打算集成Redis缓存,配置了Redis以后,发现@Cacheable注解始终不生效,折腾了大半天以后,我的心态已经崩了,决定不使用@C ...

  8. 《Java8实战》笔记(15):面向对象和函数式编程的混合-Java 8和Scala的比较

    面向对象和函数式编程的混合:Java 8和Scala的比较 Scala是一种混合了面向对象和函数式编程的语言.它常常被看作Java的一种替代语言,程序员们希望在运行于JVM上的静态类型语言中使用函数式 ...

  9. java静态方法mult_学会使用函数式编程的程序员(第3部分)

    想优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! 本系列的其它篇: 引用透明 (Referential Transparency) 引用透明是一个富有想象力的优秀术语,它是用来描述纯函数可 ...

最新文章

  1. 2014-01-04 SQL练习
  2. 【转】HashMap、TreeMap、Hashtable、HashSet和ConcurrentHashMap区别
  3. Linux内核--网络栈实现分析(二)--数据包的传递过程--转
  4. Java中深浅拷贝之List
  5. 我成功攻击了Tomcat服务器,大佬们的反应亮了
  6. [学习笔记]批次需求计划系统-简要
  7. 小学计算机教学教师培训,例谈小学信息技术课堂的有效教学
  8. 扩容是元素还是数组_Map扩容源码
  9. C语言指针-字符指针整型指针char*s int*a
  10. 在Asp.net core返回PushStream
  11. 智慧交通day02-车流量检测实现03:辅助功能(交并比and候选框的表现形式)
  12. 【软件测试】单元测试不属于动态测试
  13. 利用后退按钮进行重复提交的解决办法。
  14. 从AppCompat切换到MaterialComponents一些主题属性介绍
  15. 镁光固态硬盘用什么软件测试寿命,最简单的方法:如何查看SSD可以使用多长时间?固态硬盘寿命测试方法[详细]...
  16. 篮球计时计分器c语言程序,C51的篮球计时计分器程序
  17. XAMPP汉化教程指南
  18. 表白生成器PHP源码,表白网页在线生成源码
  19. Cocos Creator如何制作3D微信小游戏教程
  20. 基于vue的后台管理系统开发

热门文章

  1. 工程日志(110316)-机房内电子信息设备电量估算
  2. 多玩家游戏设计注意思的地方(转)
  3. java多态的好处_java萌新,对象的多态有什么好处?
  4. nuxt页面跳转_Nuxt.js错误页面跳转可能出现的问题
  5. mysql的utf8与utf8mb4 异同;utf8mb4_unicode_ci 与 utf8mb4_general_ci 如何选择
  6. jq阻止事件冒泡(点击子级不触发父级)的两种方法
  7. 一文理解JDK静态代理、JDK动态代理、Cglib动态代理
  8. 【MyBatis框架】查询缓存-二级缓存原理
  9. Linux读写缓存Page Cache
  10. Onvif之设备发现-基于gsoap2.8.27