四则运算
先来看一个计算两个整数四则运算的例子,我会这样写:
public class LambdaTest {
public static void main(String[] args) {
Integer num1 = 16, num2 = 2;
System.out.println(Calculator.add(num1, num2));
System.out.println(Calculator.minus(num1, num2));
System.out.println(Calculator.multiply(num1, num2));
System.out.println(Calculator.divide(num1, num2));
}
}
class Calculator {
public static Integer add(Integer num1, Integer num2) {
return num1 + num2;
}
public static Integer minus(Integer num1, Integer num2) {
return num1 - num2;
}
public static Integer multiply(Integer num1, Integer num2) {
return num1 * num2;
}
public static Integer divide(Integer num1, Integer num2) {
return num1 / num2;
}
}
输出:
18
14
32
8
但是如果需要计算平方,那么上面的方式就需要修改 Calculator,添加一个平方的方法,不符合开闭原则(对修改关闭,对扩展开放),那么我会像下面这样写。
public class LambdaTest {
public static void main(String[] args) {
Integer num1 = 16, num2 = 2;
System.out.println(new AddCalculator().compute(num1, num2));
System.out.println(new MinusCalculator().compute(num1, num2));
System.out.println(new MultiplyCalculator().compute(num1, num2));
System.out.println(new DivideCalculator().compute(num1, num2));
}
}
interface Calculator {
Integer compute(Integer num1, Integer num2);
}
class AddCalculator implements Calculator {
@Override
public Integer compute(Integer num1, Integer num2) {
return num1 + num2;
}
}
class MinusCalculator implements Calculator {
@Override
public Integer compute(Integer num1, Integer num2) {
return num1 - num2;
}
}
class MultiplyCalculator implements Calculator {
@Override
public Integer compute(Integer num1, Integer num2) {
return num1 * num2;
}
}
class DivideCalculator implements Calculator {
@Override
public Integer compute(Integer num1, Integer num2) {
return num1 / num2;
}
}
输出:
18
14
32
8
这里简单使用了策略模式,添加平方函数,只要新增加个类实现Calculator并计算平方就好了,对已有的代码没有修改。但是这还是有一个问题,我必须事先知道有哪些函数,我才能够用,这也是策略模式的缺点:
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类
- 客户端必须理解这些算法的区别,以便适时选择恰当的算法类
那么我们看看用java8的方式是如何解决的:
public class LambdaTest {
public static void main(String[] args) {
Integer num1 = 16, num2 = 2;
MyCalculator<Integer> calculator = new MyCalculator<>();
System.out.println(calculator.comput(num1, num2, (n1,n2)-> n1 + n2));
System.out.println(calculator.comput(num1, num2, (n1,n2)-> n1 - n2));
System.out.println(calculator.comput(num1, num2, (n1,n2)-> n1 * n2));
System.out.println(calculator.comput(num1, num2, (n1,n2)-> n1 / n2));
}
}
class MyCalculator<T> {
public T comput(T t1, T t2, BiFunction<T, T, T> function) {
return function.apply(t1, t2);
}
}
输出:
18
14
32
8
传递行为
使用java8的例子,我们可以看到,根据MyCalculator的compute方法,我们根本不知道它具体的运算规则是什么,是加法、减法还是平方等等,而这种运算行为是通过调用者传递进来的,也就是说Lambda表达式传递的是一种行为。
BiFunction
BiFunction是java8提供的一个函数式接口,它代表了一个函数,表示接收两个参数返回一个值。这与数学上的四则函数式是类似的。
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
BiFunction :apply
方法接收两个参数,分别是 T,U 类型;返回一个值,返回值类型是 R;刚才我们的四则运算的例子这三个参数类型都是一样的。我们再聚一个例子:
这个例子的功能是将数字和字符串进行拼接,返回一个字符串。
public class LambdaTest {
public static void main(String[] args) {
BiFunction<Integer, String, String> biFunction = (num, str) -> num + ":" + str;
System.out.println(biFunction.apply(888, "world"));
}
}
输出:
888:world
BiFunction还有一个默认方法andThen,在看andThen方法之前我们先看一下andThen方法的参数Function
Function
Function 同样也是一个函数式接口,它的抽象方法apply,接收一个参数,返回一个值。
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
Function:apply:
这个例子是字符串转换操作,将其转换成大写、小写:
str -> str.toUpperCase(): 表示输入一个参数,返回一个值,与Function的apply一致。
public class LambdaTest {
public static void main(String[] args) {
System.out.println(convert("Hello World", str -> str.toUpperCase()));
System.out.println(convert("Hello NodeJs", str -> str.toLowerCase()));
}
public static String convert(String str, Function<String,String> function){
return function.apply(str);
}
}
输出:
HELLO WORLD
hello nodejs
apply 的输入参数 T 和返回参数 R 也可以是不同的:
str -> Integer.parseInt(str):表示一个输入参数(String),一个返回值(Integer)符合
public class LambdaTest {
public static void main(String[] args) {
System.out.println(convert("12", str -> Integer.parseInt(str)));
}
public static Integer convert(String str, Function<String,Integer> function){
return function.apply(str);
}
}
输出:
12
Function : compose and andThen
表示function的者,优先调用参数before的apply方法,然后将before的返回值作为当前function的apply的输入参数;而andThen与之相反,优先调用当前function的apply,然后将返回值作为after的输入参数。
public class LambdaTest {
public static void main(String[] args) {
Function<Integer,Integer> function = value -> value + 3;
Function<Integer,Integer> function2 = value -> value * 2;
//优先算funtion2,所以为 1 * 2 + 3 = 5
System.out.println(function.compose(function2).apply(1));
//优先算function,所以为 (1 + 3)* 2 = 8
System.out.println(function.andThen(function2).apply(1));
}
}
输出:
5
8
BiFunction : andThen
现在我们回头看一下BiFunction的andThen方法,它接收一个Function类型的参数,先调用BiFunction的apply,然后BiFunction的返回值作为Function的输入。 那为什么BiFunction没有compose方法呢?
假如BiFunction有compose方法,那么优先调用参数before,然后返回值作为BiFunction的输入,但是BiFunction需要两个输入,方法的返回值只有一个,所以BiFunction没有compose方法。
例子:优先调用BiFunction,将两个字符串进行拼接,然后调用Function,输入一个字符串,输出一个double。
public class LambdaTest {
public static void main(String[] args) {
Function<String,Double> function = str -> Double.parseDouble(str);
BiFunction<String,String,String> biFunction = (integer, decimal) -> integer + "." + decimal;
System.out.println(biFunction.andThen(function).apply("3", "1415926"));
}
}
输出:
3.1415926
DoubleToIntFunction
Function的输入参数和输出参数是任意的,DoubleToIntFunction属于一个具体的Function,输入参数必须是double,而输出参数必须是int
@FunctionalInterface
public interface DoubleToIntFunction {
int applyAsInt(double value);
}
例子:
value -> new Double(value).intValue(): 可以表示输入一个double参数,返回一个int值
public class LambdaTest {
public static void main(String[] args) {
DoubleToIntFunction function = value -> new Double(value).intValue();
System.out.println(function.applyAsInt(3.1415926));
}
}
java.util.function 包还提供了很多Function,IntFunction、LongFunction等等,都大同小异