函数式编程
定义
函数式编程(funcational programming)属于结构化编程的一种。主要思想是把运算过程尽量写成一系列嵌套的函数调用。
结构化编程
一种编程范型,采用子程序、代码区块、for循环以及while循环 等结构来取代传统的goto
。
函数式编程关心数据的映射,命令式编程关心解决问题的步骤。
特点
函数是“第一等公民”
函数和其他数据类型一样,可以赋值给其他变量,作为其他函数的传入参数或返回值。只用“表达式”,不用“语句”
“expression” 是一个单纯的运算过程,总是有返回值。“statement”是执行某种操作,没有返回值。没有“副作用”
“副作用”是指函数内部有与外部的互动,产生运算以外的其他结果。函数要保持独立,所有功能返回一个新的值,尤其不得修改外部变量的值。
纯函数是这样一种函数,即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用。
不修改状态
函数式编程使用参数保存状态,只返回新的值,不修改系统变量。引用透明(Referential transparency)
函数的运行不依赖外部变量或“状态”,只依赖输入参数。只要参数相同,引用函数返回值总是相同的。
构成
高阶函数(higher-order function)
能够接受一个函数作为参数的函数。函子(Functor)
函子是函数式编程里最重要的数据类型,也是基本的运算单位和功能单位。
函子是指具有map
方法的容器,包含了值和变形关系。map
方法将容器里面的每一个值,映射到另一个容器。即将一个范畴转换成令一个范畴。
构成
范畴
这种数据模型的要素:
所有成员是一个集合
变形关系是函数
- 闭包(Closure)
闭包就是能够读取其他函数内部变量的函数,可简单理解成"定义在一个函数内部的函数"。当内嵌函数体内引用到体外的变量时,将会把定义时涉及到的引用环境和函数体打包成一个整体(闭包)返回。
类是有行为的数据,闭包是有数据的行为。
闭包是由函数及其相关的引用环境组合而成的实体,即闭包=函数+引用环境。
参考
函数式编程入门教程
函数式编程初探
学习Javascript闭包(Closure)
lambda
lambda表达式通常在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数。参考
Lambda表达式的本质只是一个"语法糖",由编译器推断并帮你转换包装为常规的代码,因此你可以使用更少的代码来实现同样的功能。
JAVA中的lambda
Java8之前,传递行为的唯一方法就是通过匿名内部类。Java中的lamda不是一个匿名内部类的语法糖。在匿名类中,this
指代的是匿名类本身;而在lambda表达式中,this
指代的是lambda表达式所在的这个类。
1.lambda表达式
只有那些仅仅包含一个非实例化抽象方法的接口才能使用lambda表达式。Runnable
接口就是函数式接口的一个例子。
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
Java8默认带有许多可以直接在代码中使用的函数式接口,它们位于java.util.function
包中。
2.lambda表达式结构
->
是用来把参数从函数体中分离出来的操作符。
在lambda表达式中,我们不需要明确指出参数类型,javac编译器会通过上下文自动推断参数的类型信息。根据上下文推断类型的行为称为类型推断
。
3.方法引用
当需要为一个特定方法创建lambda表达式,比如Function<String, Integer> strToLength = String::length;
,可以用缩写符号表示。String
是目标引用,::
是定界符,length
是目标引用的方法。该方法可以是静态或者实例方法。
参考原文
4.实现机制
采用在Java7中新增的动态启用
来延迟在运行时的加载策略。当javac编译代码时,捕获代码中的lambda表达式并生成一个动态启用
的调用地址(称lambda工厂)。当动态启用
被调用时,就会向lambda表达式发生转换的地方返回一个函数式接口的实例。
JAVA中的Stream
Stream是Java 8 提供的高效操作集合类(Collection
)数据的API。Stream使用一种类似用SQL语句从数据库查询数据的直观方式来提供一种对JAVA集合运算和表达的高阶抽象。
-
与
Collection
对比
Java8运行开发者使用stream
方法基于Collection集合创建一个Stream管道。Stream采用内部迭代。
Collection
与Stream
处理集合方式的不同:
懒加载
Stream不会储存数据,懒加载(只有在被使用到时才会执行计算)。Stream表达式在被末端操作
方法调用之前不会被赋值计算。
-
过渡操作
:从已存在的stream上产生另一个新的stream的函数,比如filter
,map
,sorted
等 -
末端操作
:从stream上产生一个非stream结果的函数,比如collect(toList())
,forEach
,count
,get
等