之前写过一篇java8的流操做,人们都说流操做是函数式编程,但函数式编程是什么呢?html
什么是函数式编程?它是一种编程范式,即一切都是数学函数。函数式编程语言里也能够有对象,但一般这些对象都是恒定不变的 —— 要么是函数参数,要什么是函数返回值。函数式编程语言里没有 for/next 循环,由于这些逻辑意味着有状态的改变。相替代的是,这种循环逻辑在函数式编程语言里是经过递归、把函数当成参数传递的方式实现的。java
函数式编程单看上面的理论无疑是没法理解的,因此,须要经过它的一些特色来感觉什么是函数式编程git
所谓"第一等公民"(first class),指的是函数与其余数据类型同样,处于平等地位,能够赋值给其余变量,也能够做为参数,传入另外一个函数,或者做为别的函数的返回值。
举例来讲,下面代码中的print变量就是一个函数,能够做为另外一个函数的参数。程序员
"表达式"(expression)是一个单纯的运算过程,老是有返回值;"语句"(statement)是执行某种操做,没有返回值。函数式编程要求,只使用表达式,不使用语句。也就是说,每一步都是单纯的运算,并且都有返回值。
github
缘由是函数式编程的开发动机,一开始就是为了处理运算(computation),不考虑系统的读写(I/O)。
"语句"属于对系统的读写操做,因此就被排斥在外。spring
固然,实际应用中,不作I/O是不可能的。所以,编程过程当中,函数式编程只要求把I/O限制到最小,不要有没必要要的读写行为,保持计算过程的单纯性。express
所谓"反作用"(side effect),指的是函数内部与外部互动(最典型的状况,就是修改全局变量的值),产生运算之外的其余结果。编程
函数式编程强调没有"反作用",意味着函数要保持独立,全部功能就是返回一个新的值,没有其余行为,尤为是不得修改外部变量的值.
segmentfault
上一点已经提到,函数式编程只是返回新的值,不修改系统变量
。所以,不修改变量,也是它的一个重要特色。app
在其余类型的语言中,变量每每用来保存"状态"(state)。不修改变量,意味着状态不能保存在变量中。函数式编程使用参数保存状态,最好的例子就是递归。
引用透明(Referential transparency),指的是函数的运行不依赖于外部变量或"状态",只依赖于输入的参数,任什么时候候只要参数相同,引用函数所获得的返回值老是相同的。
OO(object oriented,面向对象)是抽象数据,FP(functional programming,函数式编程)是抽象行为。
在java中,函数式编程是经过lambda表达式
实现的。
基本表达形式以下:
Runnable noArguments = () -> System.out.println("Hello World");
Lambda 表达式的基本语法是:
->
,可视为“产出”。->
以后的内容都是方法体。
- 当只用一个参数,能够不须要括号
()
。 然而,这是一个特例。- 正常状况使用括号
()
包裹参数。 为了保持一致性,也可使用括号()
包裹单个参数,虽然这种状况并不常见。- 若是没有参数,则必须使用括号
()
表示空参数列表。- 对于多个参数,将参数列表放在括号
()
中。- 方法体的语句超过一句时,须要使用
{}
,并根据状况看是否须要return
Lambda 表达式产生函数,而不是类。 在 JVM(Java Virtual Machine,Java 虚拟机)上,一切都是一个类,所以在幕后执行各类操做使 Lambda 看起来像函数 —— 但做为程序员,你能够高兴地伪装它们“只是函数”。
/** 强制 javac 检查一个接口是否符合函数接口的标准。若是该注释添加给一个枚举类型、类或另外一个注释,或者接口包含不止一个抽象方法,javac 就会报错 **/ @FunctionalInterface //该注解不是必选项 interface Yunzhi { //只包含一个抽象方法,称为函数式方法。 String test(String s); } public class LambdaExpressions { static Yunzhi yunzhi = x ->"this is a" + x ; public static void main(String[] args) { System.out.println(yunzhi.test("test")); } }
yunzhi
其实是实现了Yunzhi
的test()
,相似上面的这种接口叫函数式接口。
JDK 8 中提供了大量的函数接口,这些接口定义在java.util.function
中,所以咱们通常状况下不需再定义本身的接口,同时,各个接口的做用和名字都是相对应的,因此,了解函数式接口的命名模式就是颇有必要的了。
如下是基本命名准则:
- 若是只处理对象而非基本类型,名称则为
Function
,Consumer
,Predicate
等。参数类型经过泛型添加。- 若是接收的参数是基本类型,则由名称的第一部分表示,如
LongConsumer
,DoubleFunction
,IntPredicate
等,但基本Supplier
类型例外。- 若是返回值为基本类型,则用
To
表示,如ToLongFunction <T>
和IntToLongFunction
。- 若是返回值类型与参数类型一致,则是一个运算符:单个参数使用
UnaryOperator
,两个参数使用BinaryOperator
。- 若是接收两个参数且返回值为布尔值,则是一个谓词(Predicate)。
- 若是接收的两个参数类型不一样,则名称中有一个
Bi
。
下表描述了 java.util.function 中的目标类型(包括例外状况):
特征 | 数式方法名 | 示例 |
---|---|---|
无参数; 无返回值 |
Runnable (java.lang) run() |
Runnable |
无参数; 返回类型任意 |
Supplier get() getAs类型() |
Supplier<T> BooleanSupplier IntSupplier LongSupplier DoubleSupplier |
无参数; 返回类型任意 |
Callable (java.util.concurrent) call() |
Callable<V> |
1 参数; 无返回值 |
Consumer accept() |
Consumer<T> IntConsumer LongConsumer DoubleConsumer |
2 参数 Consumer | BiConsumer accept() |
BiConsumer<T,U> |
2 参数 Consumer; 1 引用; 1 基本类型 |
Obj类型Consumer accept() |
ObjIntConsumer<T> ObjLongConsumer<T> ObjDoubleConsumer<T> |
1 参数; 返回类型不一样 |
Function apply() To类型 和 类型To类型 applyAs类型() |
Function<T,R> IntFunction <R> LongFunction<R> DoubleFunction <R> ToIntFunction <T> ToLongFunction<T> ToDoubleFunction<T> IntToLongFunction IntToDoubleFunction LongToIntFunction LongToDoubleFunction DoubleToIntFunction DoubleToLongFunction |
1 参数; 返回类型相同 |
UnaryOperator apply() |
UnaryOperator<T> IntUnaryOperator LongUnaryOperator DoubleUnaryOperator |
2 参数类型相同; 返回类型相同 |
BinaryOperator apply() |
BinaryOperator<T> IntBinaryOperator LongBinaryOperator DoubleBinaryOperator |
2 参数类型相同; 返回整型 |
Comparator (java.util) compare() |
Comparator<T> |
2 参数; 返回布尔型 |
Predicate test() |
Predicate<T> BiPredicate<T,U> IntPredicate LongPredicate DoublePredicate |
参数基本类型; 返回基本类型 |
类型To类型Function applyAs类型() |
IntToLongFunction IntToDoubleFunction LongToIntFunction LongToDoubleFunction DoubleToIntFunction DoubleToLongFunction |
2 参数类型不一样 | Bi操做 (不一样方法名) |
BiFunction<T,U,R> BiConsumer<T,U> BiPredicate<T,U> ToIntBiFunction<T,U> ToLongBiFunction<T,U> ToDoubleBiFunction<T> |
其中最经常使用的是:
接下来实际的看看如何使用这些接口
class Foo {} class Bar { Foo f; Bar(Foo f) { this.f = f; } } public class FunctionVariants { static Function<Foo, Bar> f1 = f -> new Bar(f); public static void main(String[] args) { Bar b = f1.apply(new Foo()); } }
学到这里才忽然发现,虽然之前彻底不知道函数接口这个东西,但实际上却已经在使用他了,好比Comparator
普遍的运用在各类比较当中,pridect
也在spring的综合查询中使用过了。
虽然在宣传中,函数式编程有着巨大的优点,好比适合并行编程
、代码可靠性
和代码建立和库复用
,但在某些大佬看来:
关于函数式编程能高效建立更健壮的代码这一观点仍存在部分争议。虽然已有一些好的范例,但还不足以证实纯函数式语言就是解决编程问题的最佳方法。
无论怎样,多学一学老是好的