java函数式编程
简述
函数本质上来说就是面向过程的程序设计的基本单元,Java不支持单独定义函数,但可以把静态方法视为独立的函数,把实例方法视为自带this
参数的函数。函数式编程最早是数学家阿隆佐·邱奇研究的一套函数变换逻辑,又称Lambda Calculus(λ-Calculus),所以也经常把函数式编程称为Lambda计算。Java平台从Java 8开始,支持函数式编程和Lambda表达式。
Lambda表达式
在Java程序中,我们经常遇到一大堆单方法接口,即一个接口只定义了一个方法,例如Comparator、Runnable
以Comparator
为例,我们想要调用Arrays.sort()
时,可以传入一个Comparator
实例,以匿名类方式编写如下:
String[] array = ... Arrays.sort(array, new Comparator() { public int compare(String s1, String s2) { return s1.compareTo(s2); } });
但从Java 8开始,我们可以用Lambda表达式替换单方法接口
String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" }; Arrays.sort(array, (s1, s2) -> { return s1.compareTo(s2); });
因为是单方法接口,所以方法的参数形式已经确定,返回值类型也已确定,所以一些不必要的东西如类的定义、方法的定义可以不写
@FunctionalInterface注解
我们把只定义了单方法的接口称之为FunctionalInterface
,用注解@FunctionalInterface
标记
这个注解的作用就是用来标识这个接口只有单方法,以及让编译器检查这个接口,保证该接口只能包含一个抽象方法,否则就会编译出错
方法引用
除了Lambda表达式,我们还可以直接传入方法引用
public class Main { public static void main(String[] args) { String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" }; Arrays.sort(array, Main::cmp); System.out.println(String.join(", ", array)); } static int cmp(String s1, String s2) { return s1.compareTo(s2); } }
上述代码在Arrays.sort()
中直接传入了静态方法cmp
的引用,用Main::cmp
表示
因为Comparator
接口定义的方法是int compare(String, String)
,和静态方法int cmp(String, String)
相比,除了方法名外,方法参数一致,返回类型相同,因此,我们说两者的方法签名一致,可以直接把方法名作为Lambda表达式传入
方法引用的例子:
lambda | 等效的方法引用 |
(Apple a) -> a.getWeight() | Apple::getWeight |
() -> Thread.currentThread().dumpStack(); | Thread.currentThread()::dumpStack |
(str, i) -> str.substring(i) | String::substring |
(String s) -> System.out.println(s) | System.out::println |
可以总结为四种:
- 静态方法:类名::方法名
- 实例方法:类名::方法名
- 现有对象的实例方法:对象名::方法名
- 构造函数:类名::new
Function包
java.util.function包里定义了JDK1.8有关的函数式编程接口,常用的有:
name | type | description |
---|---|---|
Consumer | Consumer< T > | 接收T对象,不返回值 |
Predicate | Predicate< T > | 接收T对象并返回boolean |
Function | Function< T, R > | 接收T对象,返回R对象 |
Supplier | Supplier< T > | 提供T对象(例如工厂),不接收值 |
UnaryOperator | UnaryOperator | 接收T对象,返回T对象 |
BinaryOperator | BinaryOperator | 接收两个T对象,返回T对象 |
Function接口
apply方法:传入一个类型值(T)返回一个类型值(R)
R apply(T t);
本质上其实就是实现了T -> R的Lambda表达式,这里会输出hello world123
Functionfunction = new Function () { @Override public String apply(String s) { return s + "123"; } }; String str = function.apply("hello world");
用Lambda表达式能简化Function接口的实现
Functionfunction = x -> x + "123"; String str = function.apply("hello world");
Function接口同样也能用于方法参数
public class Main { public static String validateValue(String name,Functionfunction){ return function.apply(name); } public static void main(String[] args) { String name=""; System.out.println(validateValue(name,inputStr -> inputStr.isEmpty()?"不能为空":inputStr)); } }
现在name会作为validateValue方法的参数和apply方法的参数,inputStr -> inputStr.isEmpty()?"不能为空":inputStr) 是apply方法的实现
Consumer接口
accept方法:传入一个类型值,没有返回值
void accept(T t);
本质上就是让t参数干点什么,没有返回值
String name="hello world"; Consumerconsumer=new Consumer() { @Override public void accept(Object o) { System.out.println(o+"123"); } }; consumer.accept(name);
同样也可以用Lambda表达式简化
Predicate接口
Predicate一般用于做判断,返回boolean类型
boolean test(T t);
例子:
String name="aaa"; Predicatepredicate=new Predicate() { @Override public boolean test(Object o) { return o.equals("bbb"); } }; System.out.println(predicate.test(name));