1. 概述
1.1 函数式编程简介
常见的编程范式:
命令式编程(Imperative Programming),逻辑式编程(Logic Programming),函数式编程(Functional Programming)。
函数式编程作为一种编程范式,在科学领域,是一种编写计算机程序数据结构和元素的方式,它把计算过程当做是数学函数的求值,而避免更改状态和可变数据。
什么是函数式编程?简单的回答:一切都是数学函数。函数式编程语言里也可以有对象,但通常这些对象都是恒定不变的 ——
要么是函数参数,要什么是函数返回值。函数式编程语言里没有 for/next
循环,因为这些逻辑意味着有状态的改变。相替代的是,这种循环逻辑在函数式编程语言里是通过递归、把函数当成参数传递的方式实现的。
举个例子:
a = a +1
这段代码在普通成员看来并没有什么问题,但在数学家看来确实不成立的,因为它意味着变量值得改变。
1.2 Lambda 表达式简介
2.1 Lambda 表达式的形式
Java 中 Lambda 表达式一共有五种基本形式,具体如下:
➊
Runnable noArguments = () -> System.out.println("Hello World");
➋
ActionListener oneArgument = event -> System.out.println("button clicked");
➌
Runnable multiStatement = () -> { System.out.print("Hello"); System.out.println(" World");};
➍
BinaryOperator
➎
BinaryOperator
➊中所示的 Lambda 表达式不包含参数,使用空括号 () 表示没有参数。该 Lambda 表达式 实现了 Runnable
接口,该接口也只有一个 run 方法,没有参数,且返回类型为 void。➋中所示的 Lambda
表达式包含且只包含一个参数,可省略参数的括号,这和例 2-2 中的 形式一样。Lambda
表达式的主体不仅可以是一个表达式,而且也可以是一段代码块,使用大括号
({})将代码块括起来,如➌所示。该代码块和普通方法遵循的规则别无二致,可以用返 回或抛出异常来退出。只有一行代码的 Lambda
表达式也可使用大括号,用以明确 Lambda表达式从何处开始、到哪里结束。Lambda
表达式也可以表示包含多个参数的方法,如➍所示。这时就有必要思考怎样去阅 读该 Lambda
表达式。这行代码并不是将两个数字相加,而是创建了一个函数,用来计算 两个数字相加的结果。变量 add 的类型是 BinaryOperator
记住一点很重要,Lambda 表达式都可以扩写为原始的“匿名类”形式。所以当你觉得这个 Lambda 表达式很复杂不容易理解的时候,不妨把它扩写为“匿名类”形式来看。
2.2 闭包
如果你以前使用过匿名内部类,也许遇到过这样的问题。当你需要匿名内部类所在方法里的变量,必须把该变量声明为final。如下例子所示:
final String name = getUserName();
button.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent event){ System.out.println("hi "+ name); }});
Java 8放松了这一限制,可以不必再把变量声明为final,但其实该变量实际上仍然是final的。虽然无需将变量声明为 final,但在 Lambda 表达式中,也无法用作非终态变量。如果坚持用作非终态变量(即改变变量的值),编译器就会报错。
2.3 函数接口
上面例子里提到了ActionListener接口,我们看一下它的代码:
publicinterfaceActionListenerextendsEventListener{/**
* Invoked when an action occurs.
*/publicvoidactionPerformed(ActionEvent e);}
ActionListener只有一个抽象方法:actionPerformed,被用来表示行为:接受一个参数,返回空。记住,由于actionPerformed定义在一个接口里,因此abstract关键字不是必需的。该接口也继承自一个不具有任何方法的父接口:EventListener。
我们把这种接口就叫做函数接口。
JDK 8 中提供了一组常用的核心函数接口:
接口参数返回类型描述
Predicate
Consumer
Function
Supplier
UnaryOperator
BinaryOperator
其中Cosumer与Supplier对应,一个是消费者,一个是提供者。
Predicate用于判断对象是否符合某个条件,经常被用来过滤对象。
Function是将一个对象转换为另一个对象,比如说要装箱或者拆箱某个对象。
UnaryOperator接收和返回同类型对象,一般用于对对象修改属性。BinaryOperator则可以理解为合并对象。
如果以前接触过一些其他 Java 框架,比如 Google Guava,可能已经使用过这些接口,对这些东西并不陌生。所以,其实 Java 8 的改进并不是闭门造车,而是集百家之长。