柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。
看定义还是不容易理解,打个比方:一块电路板上有多个电容和电阻,要同时插上电路板才能正常工作,传统的Java定义一个方法,入参就是各种电容、电阻,返回值是电路板工作结果,如果只插入电容,这其实也是一种转态,这种转态在Java8后就可以通过函数表示,也就是返回一个电路板上面已经插好了电容,我可以插入不同的电阻返回不同的结果,把f(电容,电阻)变成了f(电容)(电阻)。
带来的好处就有:参数复用、延迟计算。
下面用代码实现:
使用Java匿名内部类实现函数式接口Function来实现柯里化函数
public class Currying {
// f(x,y,z)=x+y-z
static Function<Integer, Function<Integer, Function<Integer, Integer>>> currying = new Function<Integer, Function<Integer, Function<Integer, Integer>>>() {
@Override
public Function<Integer, Function<Integer, Integer>> apply(Integer x) {
return new Function<Integer, Function<Integer, Integer>>() {
@Override
public Function<Integer, Integer> apply(Integer y) {
return new Function<Integer, Integer>() {
@Override
public Integer apply(Integer z) {
return x + y - z;
}
};
}
};
}
};
public static void main(String[] arg) {
//一次输入xyz
System.out.println(currying.apply(10).apply(10).apply(5));
//先输入x,返回f(y,z)
Function<Integer, Function<Integer, Integer>> funcYandZ = currying.apply(10);
//再输入y,返回f(z)
Function<Integer, Integer> funcZ = funcYandZ.apply(10);
//输入z
System.out.println(funcZ.apply(5));
//换一个z输入
System.out.println(funcZ.apply(6));
}
}
上述代码通过返回确定状态的x、y的函数实现了参数的复用,最后输入z时代码才开始求值,实现了延迟计算。也体现了Java8中函数作为一等公民和变量一样,可以定义、传参、返回。
上述代码还可以通过lambda表达式简化
Function<Integer, Function<Integer, Function<Integer, Integer>>> currying1 = new Function<Integer, Function<Integer, Function<Integer, Integer>>>() {
@Override
public Function<Integer, Function<Integer, Integer>> apply(Integer x) {
return new Function<Integer, Function<Integer, Integer>>() {
@Override
public Function<Integer, Integer> apply(Integer y) {
return z -> x + y - z;
}
};
}
};
将匿名内部类一步一步替换成lambda表达式形式
Function<Integer, Function<Integer, Function<Integer, Integer>>> currying1 = new Function<Integer, Function<Integer, Function<Integer, Integer>>>() {
@Override
public Function<Integer, Function<Integer, Integer>> apply(Integer x) {
return y -> z -> x + y - z;
}
};
最终得到了级联lambda表达式的实现
Function<Integer, Function<Integer, Function<Integer, Integer>>> currying1 = x -> y -> z -> x + y - z;
再看一个实用的例子,判断字符串是否在list中存在
/**
* 判断list中元素是否存在以某个字符串开头的元素
*/
static Function<List<String>, Predicate<String>> listExist = x -> y -> x.stream().anyMatch(s -> s.startsWith(y));
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("zhang san");
list.add("li si");
list.add("wang wu");
Predicate<String> predicate = listExist.apply(list);
System.out.println(predicate.test("zhang")); // ture
//直接复用
System.out.println(predicate.test("zhou")); // false
}