写了很久的java8,也使用过函数式接口,但是一直云里雾里的,最近在接触过python/js/c++后比较明朗了。
假定我们有这样一个场景:给定两个数,在不同的处理流程中,有时候计算和,有时候又需要计算差,我们希望在计算的时候,要做好参数校验
在python中我们可以这样处理:
def sum(a, b):
return a + b
def sub(a, b):
return a - b
def math_calculate(a, b, func):
# 做一些公共的逻辑处理,比如参数校验
if a is None or b is None:
return None
return func(a, b)
sum_result = math_calculate(100, 20, sum)
print(sum_result)
sub_result = math_calculate(100, 20, sub)
print(sub_result)
运行该python脚本,输出结果是:120, 80
math_calculate函数的第三个参数,传递的是一个函数名,那么在进行func(a,b)的时候,实际上就是执行传递的函数逻辑。比如我传递的是sum,那么math_calculate函数实际上执行的是sum(a,b)。
math_calculate函数的好处是,把参数的校验统一起来了,不用分散到sum和sub这两个函数中。
在c/c++中我们可以定义函数指针,逻辑类似。
但是在java中呢,却不能给方法传递方法名。我们通常的做法是定义一个抽象类/接口,然后子类实现其中的方法,来实现,比如:
public abstract class MathCalculate {
protected abstract Integer calculate(Integer a, Integer b);
}
public class SumMathCalculate extends MathCalculate {
@Override
protected Integer calculate(Integer a, Integer b) {
return a + b;
}
}
public class SubMathCalculate extends MathCalculate {
@Override
protected Integer calculate(Integer a, Integer b) {
return a - b;
}
}
public class Test {
public static Integer mathCalculate(Integer a, Integer b, MathCalculate calculateImpl) {
if (null == a || b == null) {
return null;
}
return calculateImpl.calculate(a, b);
}
public static void main(String[] args) {
SumMathCalculate sum = new SumMathCalculate();
SubMathCalculate sub = new SubMathCalculate();
mathCalculate(100, 20, sum);
mathCalculate(100, 20, sub);
}
}
在Test类中的mathCalculate第三个参数,传递的是对象。
表面上看这样实现,为了一个简单的计算逻辑,还得定义三个类,然后串起来,有点"太重了"的感觉。其实我们只要对象中的函数,不希望有对象!
Java8函数式接口,就是来优化这一点的!
@FunctionalInterface
interface MathCalculate2 {
Integer calculate(Integer a, Integer b);
}
public class Test2 {
public static Integer mathCalculate(Integer a, Integer b, MathCalculate2 calculateFunc) {
if (null == a || b == null) {
return null;
}
return calculateFunc.calculate(a, b);
}
public static void main(String[] args) {
mathCalculate(100, 20, (a, b) -> a + b);
mathCalculate(100, 20, (a, b) -> a - b);
}
}
注意,此时MathCalculate2,加了@FunctionalInterface,表明是一个函数式接口,不再希望像通常使用接口的方式来使用,而是要传入一个lamda表达式(也就是函数的真正实现)。
这样下次如果需要乘法和除法,或者其他的二目运算,那么只需要传递函数实现就是了,不需要定义类了!