java - 非捕获 lambda 似乎仍然捕获了封闭的实例

标签 java lambda java-8 language-lawyer

我写了这段代码:

import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.function.Supplier;

public class Main {
    public static void main(String[] args) throws Exception {
        new Main();
    }

    private Main() throws Exception {
        Supplier<Thread> supplier = (Supplier<Thread> & Serializable) () -> new Thread() {};
        new ObjectOutputStream(System.out).writeObject(supplier);
    }
}

如果我运行它,我会得到一个异常:

Exception in thread "main" java.io.NotSerializableException: Main
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1378)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at Main.<init>(Main.java:28)

但是,我的理解是我正确地制作了 lambda Serializable 并且我声明它不引用任何周围的上下文,因此是一个非捕获 lambda 。然而 Main 实例被捕获并且 lambda 表达式的结果未能被序列化。我意识到我在 lambda 中声明了一个匿名类,但我希望 lambda 实例本身是它的封闭实例,而不是周围的 Main 类型。

这种行为是 Java 语言规范所期望的吗?如果是,为什么会这样?

最佳答案

在你的 lambda 表达式的主体中,你有匿名类声明 new Thread() {} 并且你不在 static 上下文中,所以这个表达式隐式捕获 this,根据 JLS §15.27.2, Lambda Body,它在 lambda 表达式内部与外部具有相同的含义:

Unlike code appearing in anonymous class declarations, the meaning of names and the this and super keywords appearing in a lambda body, along with the accessibility of referenced declarations, are the same as in the surrounding context (except that lambda parameters introduce new names).

The transparency of this (both explicit and implicit) in the body of a lambda expression - that is, treating it the same as in the surrounding context - allows more flexibility for implementations, and prevents the meaning of unqualified names in the body from being dependent on overload resolution.

由于周围的上下文决定了匿名类的行为,您可以通过使用 static 上下文来创建嵌套类来轻松解决此问题:

import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.function.Supplier;

public class Main {
    public static void main(String[] args) throws Exception {
        write();
    }

    static void write() throws Exception {
        Supplier<Thread> supplier = (Supplier<Thread> & Serializable)() -> new Thread() {};
        new ObjectOutputStream(System.out).writeObject(supplier);
    }
}

然后,周围的实例将不会被捕获。

请注意,这可以看作是 lambda 表达式的一般思想,将函数定义为表达式,除了函数参数的引入之外,它们的含义与它们所写的上下文中的含义完全相同。函数接口(interface)实例的生成只是将这个概念以兼容和有用的方式引入 Java 编程语言的载体,而不是影响 lambda 表达式含义的概念。

关于java - 非捕获 lambda 似乎仍然捕获了封闭的实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44412284/

相关文章:

c# - lambda 表达式中带有 Join 的Where 子句

c++ - VS2010 中 C++ lambda 表达式的奇怪错误(变量 y1)

java-8 - 用双列表示法结合函数和消费者

java - Android MapView - 如何获取 map 选定部分的中心?

java - 将三元运算符与 int 和 boolean 一起使用时出错

java - 以编程方式导入 C/C++ 项目 eclipse

java - 在 Java 中,流相对于循环的优势是什么?

java - 仅显示最近添加的对象的数组列表

c++ - "type-switch"在 C++11 中构造

java - 无法从 TemporalAccessor 获取 OffsetDateTime