关于 Java 的闭包

之前未能理解 Java 对闭包的说明中“effective final”的意思,现在对其进行了一番针对性学习。

这个词(实际上的 final)应当这样理解——在闭包中不能改变外界变量的“值”,这个“值”对基本类型来说是值,对引用类型来说是引用。这个问题的影响其实并不大——如果需要对变量的值进行改变,将其传入一个数组即可。

1
2
3
4
5
6
7
8
9
10
11
Supplier<Integer> Counter() { // 也可使用 IntSupplier
int[] count = {0};
return () -> {
count[0]++; // 捕获
return count[0];
};
}
void main(String[] args) {
Supplier<Integer> counter = Counter();
System.out.println(counter.getAsInt());
}

为什么不能改变外界变量的值?这里可以这样解释(事实上也大概就是如此)——Java 的闭包并非真正捕获外界变量(这种捕获是捕获了 C++风格的真正的引用,js,scheme 等语言的闭包都是如此,对这些语言来说,它们所定义的函数看待上层作用域中的变量,就像 if,for 块看待外层块作用域的时候一样),而是把对象的值复制(对基本类型是值,对对象来说是指针)并保存为自身的一个属性,所以如果原值改变,捕获值和原值就不同了,这就和闭包性质所要求的不一致了(这时候的性质类似函数调用,作为函数参数传入),因此禁止了被捕获对象的重新赋值。

Java 中的 lambda 表达式是残废的——要使用它必须先定义相应函数式接口,而不能像 kotlin,ts 等语言有所谓函数类型,可以直接应用,且对其的应用(apply)必须要通过其方法,这是比较麻烦的。

但是这些限制实际上影响不大——需要自定义接口的情况是较少的,Java 本身提供了比较丰富的接口定义可以直接使用,但是代价是增加理解成本;客户代码一般不会预先定义相关类型的变量并自己使用,而是直接通过 lambda 表达式构造相应实例并直接传递给相关方法中,将其的调用交由相关方法进行实现。

而 Java 的 lambda 的最大缺陷在于——其只能抛出在签名中指明能够抛出的异常!也就是说,如果用户自定义的 lambda 表达式可能出现异常,他必须进行 try-catch!这是极为不便的,目前的解决方案唯有定义自己的函数式接口用于抛出异常!这可不是我们想要的答案——对集合类型的 map-filter-reduce 操作中如果有异常,就必须要 try-catch,无法改变!这是无法理喻的,特别是当我们在 Spring 环境下工作,依赖全局异常处理来进行异常的时候,或者说将异常当作一种别样的控制流来处理的时候。

Java8 提供的java.util.function包包含了许多可以直接应用的函数式接口,可以应付绝大部分简单业务情况——(下表引用于网络)

1 BiConsumer<T,U> 代表了一个接受两个输入参数的操作,并且不返回任何结果
2 BiFunction<T,U,R> 代表了一个接受两个输入参数的方法,并且返回一个结果
3 BinaryOperator 代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果
4 BiPredicate<T,U> 代表了一个两个参数的 boolean 值方法
5 BooleanSupplier 代表了 boolean 值结果的提供方
6 Consumer 代表了接受一个输入参数并且无返回的操作
7 DoubleBinaryOperator 代表了作用于两个 double 值操作符的操作,并且返回了一个 double 值的结果。
8 DoubleConsumer 代表一个接受 double 值参数的操作,并且不返回结果。
9 DoubleFunction 代表接受一个 double 值参数的方法,并且返回结果
10 DoublePredicate 代表一个拥有 double 值参数的 boolean 值方法
11 DoubleSupplier 代表一个 double 值结构的提供方
12 DoubleToIntFunction 接受一个 double 类型输入,返回一个 int 类型结果。
13 DoubleToLongFunction 接受一个 double 类型输入,返回一个 long 类型结果
14 DoubleUnaryOperator 接受一个参数同为类型 double, 返回值类型也为 double 。
15 Function<T,R> 接受一个输入参数,返回一个结果。
16 IntBinaryOperator 接受两个参数同为类型 int, 返回值类型也为 int 。
17 IntConsumer 接受一个 int 类型的输入参数,无返回值 。
18 IntFunction 接受一个 int 类型输入参数,返回一个结果 。
19 IntPredicate :接受一个 int 输入参数,返回一个布尔值的结果。
20 IntSupplier 无参数,返回一个 int 类型结果。
21 IntToDoubleFunction 接受一个 int 类型输入,返回一个 double 类型结果 。
22 IntToLongFunction 接受一个 int 类型输入,返回一个 long 类型结果。
23 IntUnaryOperator 接受一个参数同为类型 int, 返回值类型也为 int 。
24 LongBinaryOperator 接受两个参数同为类型 long, 返回值类型也为 long。
25 LongConsumer 接受一个 long 类型的输入参数,无返回值。
26 LongFunction 接受一个 long 类型输入参数,返回一个结果。
27 LongPredicate R 接受一个 long 输入参数,返回一个布尔值类型结果。
28 LongSupplier 无参数,返回一个结果 long 类型的值。
29 LongToDoubleFunction 接受一个 long 类型输入,返回一个 double 类型结果。
30 LongToIntFunction 接受一个 long 类型输入,返回一个 int 类型结果。
31 LongUnaryOperator 接受一个参数同为类型 long, 返回值类型也为 long。
32 ObjDoubleConsumer 接受一个 object 类型和一个 double 类型的输入参数,无返回值。
33 ObjIntConsumer 接受一个 object 类型和一个 int 类型的输入参数,无返回值。
34 ObjLongConsumer 接受一个 object 类型和一个 long 类型的输入参数,无返回值。
35 Predicate 接受一个输入参数,返回一个布尔值结果。
36 Supplier 无参数,返回一个结果。
37 ToDoubleBiFunction<T,U> 接受两个输入参数,返回一个 double 类型结果
38 ToDoubleFunction 接受一个输入参数,返回一个 double 类型结果
39 ToIntBiFunction<T,U> 接受两个输入参数,返回一个 int 类型结果。
40 ToIntFunction 接受一个输入参数,返回一个 int 类型结果。
41 ToLongBiFunction<T,U> 接受两个输入参数,返回一个 long 类型结果。
42 ToLongFunction 接受一个输入参数,返回一个 long 类型结果。
43 UnaryOperator 接受一个参数为类型 T, 返回值类型也为 T。

本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 协议 ,转载请注明出处!