这篇 IBM 论文提供了 BGGA 和 CICE 之间语法差异的很好示例:
BGGA 提案
BGGA 提案创建了函数类型的概念,其中函数具有类型化参数列表、返回类型和 throws 子句。在 BGGA 提案中,平方和代码类似于清单 9 中的代码:
清单 9。使用 BGGA 闭包语法计算平方和
sumOfSquares = mapReduce(myBigCollection,
{ Double x => x * x },
{ Double x, Double y => x + y });
=> 符号左侧的大括号内的代码标识参数的名称和类型;右边的代码表示正在定义的匿名函数的实现。此代码可以引用块内定义的局部变量、闭包的参数或创建闭包的范围内的变量。
在 BGGA 提案中,您可以将变量、方法参数和方法返回值声明为函数类型。您可以在任何需要单个抽象方法类(如 Runnable 或 Callable)实例的上下文中提供闭包;对于匿名类型的闭包,提供了一个 invoke() 方法,因此您可以使用指定的参数列表调用它们。
BGGA 提案的主要目标之一是允许程序员创建类似于控制结构的方法。因此,BGGA 还提出了一些语法糖,允许您调用接受闭包的方法,就好像它们是新关键字一样,这样您就可以创建 withLock() 或 forEach() 之类的方法并像控制原语一样调用它们。清单 10 显示了如何在 BGGA 提案下定义 withLock() 方法;清单 11 和清单 12 显示了如何使用标准形式和“控制构造”形式来调用它:
清单 10。在 BGGA 闭包提案下编写 withLock() 方法
public static <T,throws E extends Exception>
T withLock(Lock lock, {=>T throws E} block) throws E {
lock.lock();
try {
return block.invoke();
} finally {
lock.unlock();
}
}
清单 10 中的 withLock() 方法接受一个锁和一个闭包。闭包的返回类型和 throws 子句是通用参数;编译器中的类型推断通常允许在不指定 T 和 E 的值的情况下调用它,如清单 11 和清单 12 所示:
清单 11。调用 withLock()
withLock(lock, {=>
System.out.println("hello");
});
清单 12。使用控制构造简写调用 withLock()
withLock(lock) {
System.out.println("hello");
}
与泛型一样,BGGA 提案下的大部分闭包复杂性由库创建者承担;使用接受闭包的库方法要简单得多。
BGGA 提案还致力于修复在尝试使用内部类实例来获得闭包的好处时出现的许多透明度故障。例如,return、break 和 this 在代码块中的语义与在表示同一代码块的 Runnable(或其他内部类实例)中的语义不同。在迁移代码以利用通用算法时,这些不透明的元素可能会导致混淆。
CICE 提案
CICE 提案是一个更简单的提案,它解决了实例化内部类实例过于繁琐的问题。它没有创建函数类型的概念,而是创建了一种更紧凑的语法,用于使用单个抽象方法(例如 Runnable、Callable 或 Comparator)实例化内部类的实例。
清单 13 显示了 CICE 下平方和代码的样子。它明确了 mapReduce() 使用的 UnaryFunction 和 BinaryFunction 类型。mapReduce() 的参数是派生自 UnaryFunction 和 BinaryFunction 的匿名类;该语法简单地省略了与创建匿名实例相关的大部分冗余。
清单 13。CICE 关闭提案下的平方和代码
Double sumOfSquares = mapReduce(myBigCollection,
UnaryFunction<Double>(Double x) { return x*x; },
BinaryFunction<Double, Double>(Double x, Double y) { return x+y; });
因为表示传递给 mapReduce() 函数的对象是普通的匿名类实例,所以它们的主体可以引用在封闭范围内定义的变量;清单 13 和清单 7 中的方法之间的唯一区别是语法的冗长。