1. 此函数非彼函数
在弄清楚什么是函数编程之前,有必要先弄清楚究竟什么是函数这个问题。在面向对象编程中,我们经常将方法称为函数,那么方法与函数究竟是否是同一个东西的不同称呼呢?函数式编程中的“函数”是指数学意义上的函数,不是编程语言中的“函数”。数学上的函数(Function)可以看成一个小机器,给这个机器提供一定的原材料(输入参数),它就会加工出(输出)一定的产品(返回值),如图1所示。
图1 函数示意图
图片来自:https://en.wikipedia.org/wiki/Function_(mathematics)
方法往往是有副作用(Side Effect)的,即一个方法的调用往往意味着对象状态的变化,而这种变化可能使得用同样的参数多次调用同一个方法,每次调用的返回值可能各不相同。而数学上的函数是没有副作用的(Side Effect Free),因此只要使用同样的参数调用同一个函数,那么不管调用这个函数多少次,每次调用的返回值总是相同的。例如,对于一个计算矩形面积的函数f(x,y)=x*y(x和y代表矩形的长和宽),那么无论调用多少次f(3,4),每次的返回值总是12。
从“无副作用”的角度来看,一个方法只要对其的调用不会产生副作用,那么它就是一个函数,否则它就不是函数。例如,String.charAt(int)这个方法就是一个函数。System.currentTimeMillis()这个方法就不是函数,因为对其的每次调用的返回值都可能不相同。
2. 函数式编程:函数作为“一等公民”
面向对象编程是Java平台在Java 8之前就已经支持的一种编程范式(Paradigm),Java平台从Java 8开始支持另外一种编程范式——函数式编程(Functional Programming)。面向对象编程中,对象是“一等公民”。所谓“一等公民”就是指可以作为“值”的语言元素。“值”既可以赋值给变量,也可以作为方法调用的参数进行传递以及作为方法的返回值。例如,我们可以将“1024”赋值给一个int型变量,也可以将其作为方法调用的参数,或者作为一个方法的返回值。同样,对象(确切的说是对对象的引用,相当于指针)也可以作为一个变量(引用型变量)值或者作为一个方法调用的参数,或者作为一个方法的返回值(返回一个对象的方法我们通常称之为工厂方法)。
在面向对象编程中,方法(或者函数)并非“一等公民”,因为方法在这里就不是一个值。而在函数式编程中,函数翻身成为“一等公民”,因此在这里函数可以作为一个值赋值给一个变量,可以作为函数调用的参数(即用函数作为参数去调用一个函数),可以作为函数的返回值(即一个函数的返回值是另外一个函数)。
3. 函数式编程的优势
3.1. 编写更为简洁的代码
函数式编程使得我们能够编写更为简洁的代码。下面看一个例子。假设有个服务(如清单1所示),其启动动作(即Service.start()方法)比较耗时,因此我们希望用异步的方式去启动这个服务,以避免主线程被阻塞。
清单1 一个启动比较耗时的服务
public class SomeTimeConsumingService implements Service {
@Override
public void start() {
// 模拟启动动作的耗时
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
;
}
}
}
为此,我们只需要创建一个专门的线程(工作者线程),并在该线程的run方法中调用Service.start()即可,如清单2所示。
清单2 以异步方式启动一个耗时服务(面向对象编程)
public class ServiceStarter {
public static void main(String[] args) {
Thread serviceStarter;
final SomeTimeConsumingService service = new SomeTimeConsumingService();
//创建Runnable实例以调用Service.start()
serviceStarter = new Thread(new Runnable() {
@Override
public void run() {
service.start();
}
});
//启动线程
serviceStarter.start();
}
}
清单2中我们创建的匿名Runnable实例的目的仅仅是为了在工作者线程中调用Service.start()而已。如果我们能够直接把Service.start()方法作为一个参数传递给Thread类的构造器(姑且将构造器看做一种特殊的方法),那么这里我们也就省却了创建Runnable实例,从而使代码更加简洁。在Java 8之后我们的确可以这么做,如清单3所示。
清单3 以异步方式启动一个耗时服务(函数式编程)
public class ServiceStarterFP {
public static void main(String[] args) {
Thread serviceStarter;
final SomeTimeConsumingService service = new SomeTimeConsumingService();
serviceStarter = new Thread(service::start);//直接将start方法作为参数传递给构造器
serviceStarter.start();
}
}
这里,“service::start”是一个方法引用(Method Reference),它表示对象service的start方法。可见,我们将一个方法作为一个参数传递给了Thread类的构造器,这使得清单3相比清单2中相应代码要简洁一些。读者也许在想,Thread类的构造器的参数的类型是java.lang.Runnable,何以我们能够将一个方法(作为“值”)作为其参数传入呢?这样为何不会出现类型不匹配(兼容)的问题呢?后续的笔记中我们会解释这一点。
从这个例子我们可以看到,Java 8中方法已然跻身作为函数的“一等公民”行列,因此方法可以作为参数进行传递。显然Service.start()并不是真正意义上函数,因为它是有副作用的(至少它有可能是有副作用的,因为它是用来启动一个服务的)。尽管如此,这个方法还是享受到了函数的“一等公民”待遇(作为值传递)。由此可见,Java 8中一个方法可以看做是一个函数,但是Java并不强制这个方法必须是无副作用的。
3.2. 函数式编程为并发编程提供了便利
函数式编程为并发编程提供了便利。函数是无副作用的,这就意味着这函数必须是无状态(Stateless)。无状态是多线程编程和函数式编程的共同好友。我们知道,在多线程编程中,如果多个线程之间存在共享可变状态(Shared Mutable State),那么为了确保线程安全我们往往需要借助锁,而锁除了其开销较大之外还存在可能导致死锁等问题。相反,如果多个线程之间不存在共享状态(或者仅存在只读状态),那么这些线程是可以并行(Parallel)的。这不仅有利于提高系统的并发性,也使得代码更为简单。函数也是类似,既然函数是(必须是)无状态的,那么多个线程同时执行一个函数的时候也就无需加锁,这既简化了代码又有利于提供系统的并发性。
4. 作者简介
黄文海,著有《Java多线程编程实战指南(核心篇)》、《Java多线程编程实战指南(设计模式篇)》。
5. 参考资料
1.Raoul-Gabriel Urma等.Java 8实战.人民邮电出版社,2016
2.黄文海.Java多线程编程实战指南(核心篇).电子工业出版社,2017
3.黄文海.Java多线程编程实战指南(设计模式篇).电子工业出版社,2015
相关推荐
Java 8函数式编程学习笔记
函数式数据处理 第三部分 高效Java 8编程 第四部分 超越Java 8 第一部分 基础知识 1 2 3 第二部分 函数式数据处理 4 5 6 7 第三部分 高效Java 8编程 8 9 10 11 12 第四部分 超越Java 8 13 14 15 16 附录
Scala 是一门多范式(multi-paradigm)的编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性。 Scala 运行在Java虚拟机上,并兼容现有的Java程序。 Scala 源代码被编译成Java字节码,所以它可以运行于...
Scala是一种多范式的编程语言,其设计的初衷是要集成面向对象编程和函数式编程的各种特性。Scala运行于Java平台(Java虚拟机),并兼容现有的Java程序。
同上述的Java8函数式编程一起看的,相比上面的书,这本书难度偏低,内容都是JavaSe8的新特性,适合快速熟悉JavaSe8。 JavaWeb Sprint Boot2 精髓 从构建小系统到架构分布式大系统() 缺点:书名有点夸张,作者喜欢...
行矩阵运算、绘制函数和数据、实现算法、创建用户界面、连接其他编程语言的程序等。MATLAB的基本数据单位是矩阵,它的指令表达式与数学、工程中常用的形式十分相似,故用MATLAB来解算问题要比用C,FORTRAN等语言完成...
韩顺平oracle学习笔记 第0讲:如何学习oracle 一、如何学习oracle Oracle目前最流行的数据库之一,功能强大,性能卓越。学习oracle需要具备一定基础: 1.学习过一门编程语言(如:java ,c) 2.最好学习过一门别的...
在Java 8中,得益于Lambda所带来的函数式编程,引入了一个全新的Stream概念,用于解决已有集合类库既有的弊端。 1.1 引言 传统集合的多步遍历代码几乎所有的集合(如Collection接口或Map接口等)都支持直接或间接的...
《Java 8函数式编程》_王群锋译_2015-04-01 《Java JDK 9学习笔记》_林信良_2018-06-01 《Neo4j 3.x入门经典》_张帜等_2019-02-01 《分布式系统常用技术及案例分析(第2版)》_柳伟卫_2018-12-01
事实证明,学习Rx的关键是训练自己使用函数式编程来操纵集合。 函数式编程为开发人员提供了将常见集合操作抽象为可重用,可组合的构建块的工具。 您会惊讶地发现,您对集合执行的大多数操作都可以通过五个简单的...
《深入理解Java虚拟机》、《并发编程的艺术》、《Java多线程核心编程艺术》、《Java8函数式编程》、《Redis设计与实现》、《RocketMQ技术内幕》、《Spring技术内幕》、《Spring源码深度解析》、《剑指Offer》、...
中国移动重庆java笔试题 说明 平时的学习资源都比较的凌乱...函数式编程 Underscore Typescript webpack 源码 Parcel Gulp es系列 Babel HTTP http 请求库 Promise NodeJS MongoDB Git canvas 移动端 技术类 微信小程序
程序员考试刷题java8-ocp-study-notes 跟踪 OCP Java 8 书籍学习指南的存储库 ...函数式编程 第 5 章 - 日期、字符串和本地化 第 6 章 - 异常和断言 第 7 章 - 并发 第 8 章 - IO 第 9 章 - NIO.2 第 10 章 - JDBC
JS函数式编程指南 JavaScript Promise迷你书(中文版) 腾讯移动Web前端知识库 Front-End-Develop-Guide 前端开发指南 前端开发笔记本 大前端工具集 - 聂微东 前端开发者手册 入门类 地址 前端入门教程 瘳雪峰的...
JS函数式编程指南 JavaScript Promise迷你书(中文版) 腾讯移动Web前端知识库 Front-End-Develop-Guide 前端开发指南 前端开发笔记本 大前端工具集 - 聂微东 前端开发者手册 入门类 入门类 地址 前端入门教程 瘳...
函数式编程 :butterfly:Nodejs Nest :lady_beetle:TypeScript :lizard:Vue :dolphin:React react :mobile_phone:跨端应用 React-Native Flutter :rocket:Webpack :automobile: Gulp :construction:规范化 :tractor:...
Scala 是一门多范式(multi-paradigm)的编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性。Scala 运行在Java虚拟机上,并兼容现有的Java程序。java、Scala都是基于JVM的编程语言(文件编译成class...
JS函数式编程指南 JavaScript Promise迷你书(中文版) 腾讯移动Web前端知识库 Front-End-Develop-Guide 前端开发指南 前端开发笔记本 大前端工具集 - 聂微东 前端开发者手册 入门类 入门类 地址 前端入门教程 瘳...
前端知识体系(zh-ch) Github文档库更新慢,最新内容请关注语雀: ...函数式编程 [设计模式]:MVC/MVVM/MVP 前端开发工具 构建工具 webpack grunt gulp npm yarn 测试工具 Puppeteer Jest mocha 调试