Java8---隐示final

来源:互联网 发布:centos镜像配置yum源 编辑:程序博客网 时间:2024/06/05 00:51

捕获和非捕获的Lambda表达式

当Lambda表达式访问一个定义在Lambda表达式体外的非静态变量或者对象时,这个Lambda表达式称为“捕获的”。比如,下面这个lambda表达式捕捉了变量x:

int x = 5; return y -> x + y;
为了保证这个lambda表达式声明是正确的,被它捕获的变量必须是“有效final”的。所以要么它们需要用final修饰符号标记,要么保证它们在赋值后不能被改变。

Lambda表达式是否是捕获的和性能悄然相关。一个非不捕获的lambda通常比捕获的更高效,虽然这一点没有书面的规范说明(据我所知),而且也不能为了程序的正确性指望它做什么,非捕获的lambda只需要计算一次. 然后每次使用到它都会返回一个唯一的实例。而捕获的lambda表达式每次使用时都需要重新计算一次,而且从目前实现来看,它很像实例化一个匿名内部类的实例。



lambdas不做的事

你应该记住,有一些lambdas不提供的特性。为了Java 8它们被考虑到了,但是没有被包括进去,由于简化以及时间限制的原因。

Non-final* 变量捕获 - 如果一个变量被赋予新的数值,它将不能被用于lambda之中。"final"关键字不是必需的,但变量必须是“有效final”的(前面讨论过)。这个代码不会被编译:

int count = 0;List<String> strings = Arrays.asList("a", "b", "c");strings.forEach(s -> {    count++; // error: can't modify the value of count });

例外的透明度 - 如果一个已检测的例外可能从lambda内部抛出,功能性的接口也必须声明已检测例外可以被抛出。这种例外不会散布到其包含的方法。这个代码不会被编译:

void appendAll(Iterable<String> values, Appendable out) throws IOException { // doesn't help with the error values.forEach(s -> {        out.append(s); // error: can't throw IOException here // Consumer.accept(T) doesn't allow it });}

有绕过这个的办法,你能定义自己的功能性接口,扩展Consumer的同时通过像RuntimeException之类抛出 IOException。我试图用代码写出来,但发现它令人困惑是否值得。

控制流程 (break, early return) -在上面的 forEach例子中,传统的继续方式有可能通过在lambda之内放置 "return;"来实现。但是,没有办法中断循环或者从lambda中通过包含方法的结果返回一个数值。例如:

final String secret = "foo"; boolean containsSecret(Iterable<String> values) {    values.forEach(s -> { if (secret.equals(s)) {            ??? // want to end the loop and return true, but can't }    });}

进一步阅读关于这些问题的资料,看看这篇Brian Goetz写的说明:在 Block<T>中响应“已验证例外”

0 0