Java8 教程

Java8 Lambda 变量捕获

在某些情况下,Java lambda 表达式能够访问在 lambda 函数体之外声明的变量。

Java lambda 可以捕获以下类型的变量:

  • 局部变量

  • 实例变量

  • 静态变量

下文将逐一介绍这些变量捕获方式。

局部变量捕获

Java lambda 表达式可以捕获在 lambda 主体之外声明的局部变量的值。为了说明这一点,请先看一下这个单一方法接口:

public interface MyFactory {
    public String create(char[] chars);
}

现在,请看这个实现了 MyFactory 接口的 lambda 表达式:

MyFactory myFactory = (chars) -> {
    return new String(chars);
};

现在,这个 lambda 表达式只引用传给它的参数值(字符)。但我们可以改变这一点。下面是一个更新版本,它引用了在 lambda 函数体之外声明的字符串变量:

// myString 实际上是一个 final 修饰的变量
String myString = "Test";

MyFactory myFactory = (chars) -> {
    return myString + ":" + new String(chars);
};

如您所见,lambda 主体现在引用了在 lambda 主体之外声明的局部变量 myString。只有当且仅当被引用的变量是“有效的最终变量”(即在赋值后不会改变其值)时,才有可能这样做。如果 myString 变量的值后来发生了变化,编译器就会抱怨 lambda 主体内部对它的引用。

实例变量捕获

Java lambda 表达式还可以捕获创建 lambda 的对象中的实例变量。下面的示例可以说明这一点:

public class EventConsumerImpl {

    private String name = "MyConsumer";

    public void attach(MyEventProducer eventProducer){
        eventProducer.listen(e -> {
            System.out.println(this.name);
        });
    }
}

请注意 lambda 主体中对 this.name 的引用。这将捕获外层 EventConsumerImpl 对象的 name 实例变量。甚至可以在捕获后更改实例变量的值,而该值将反映在 lambda 中。

这实际上是 Java lambda 与匿名接口实现不同的地方之一。匿名接口实现可以拥有自己的实例变量,并通过 this 引用进行引用。但是,lambda 不能拥有自己的实例变量,因此 this 总是指向外层对象。

注意:上述事件消费者的设计并不特别优雅。我这样做只是为了说明实例变量捕获。

静态变量捕获

Java lambda 表达式也可以捕获静态变量。这并不奇怪,因为只要静态变量是可访问的(包作用域或公共变量),Java 应用程序中的任何地方都可以访问静态变量。

下面是一个创建了 lambda 的示例类,它在 lambda 主体内部引用了一个静态变量:

public class EventConsumerImpl {
    private static String someStaticVar = "Some text";

    public void attach(MyEventProducer eventProducer){
        eventProducer.listen(e -> {
            System.out.println(someStaticVar);
        });
    }
}

静态变量的值也允许在 lambda 捕捉后发生变化。

同样,上述类的设计有点不合理。不要想太多。这个类的主要作用是向你展示 lambda 可以访问静态变量。

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号