Java 不捕获lambda似乎仍然捕获了封闭实例
我写了这段代码:Java 不捕获lambda似乎仍然捕获了封闭实例,java,lambda,java-8,language-lawyer,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 Excep
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);
}
}
import java.io.ObjectOutputStream;
导入java.io.Serializable;
导入java.util.function.Supplier;
公共班机{
公共静态void main(字符串[]args)引发异常{
新的Main();
}
private Main()引发异常{
Supplier=(Supplier&Serializable)(->new Thread(){};
新ObjectOutputStream(System.out).writeObject(供应商);
}
}
如果我运行它,我将得到一个异常:
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)
线程“main”中的异常java.io.NotSerializableException:main
位于java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
位于java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1378)
位于java.io.ObjectOutputStream.WriteObject 0(ObjectOutputStream.java:1174)
位于java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
位于java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
位于java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
位于java.io.ObjectOutputStream.WriteObject 0(ObjectOutputStream.java:1178)
位于java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
在Main(Main.java:28)
然而,我的理解是,我正确地将lambda序列化,并声明它不引用任何周围的上下文,因此它是非捕获lambda。然而,Main
实例被捕获,lambda表达式的结果无法序列化。我意识到我在lambda中声明了一个匿名类,但我希望lambda实例本身是它的封闭实例,而不是周围的Main
类型
Java语言规范是否期望这种行为?如果是,原因是什么?在lambda表达式的主体中,您有匿名类声明new Thread(){}
,并且您不在静态上下文中,因此此表达式隐式捕获this
,其在lambda表达式内的含义与在lambda表达式外的含义相同,根据:
与匿名类声明中出现的代码不同,lambda正文中出现的名称和this
和super
关键字的含义以及引用声明的可访问性与周围上下文中的含义相同(除了lambda参数引入新名称)
lambda表达式主体中的this
(显式和隐式)的透明性(即,将其视为与周围上下文中相同的内容)允许实现更大的灵活性,并防止主体中非限定名称的含义依赖于重载解析
由于周围的上下文决定了匿名类的行为,因此可以通过使用静态
上下文创建嵌套类来轻松解决此问题:
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);
}
}
import java.io.ObjectOutputStream;
导入java.io.Serializable;
导入java.util.function.Supplier;
公共班机{
公共静态void main(字符串[]args)引发异常{
write();
}
静态void write()引发异常{
Supplier=(Supplier&Serializable)(->new Thread(){};
新ObjectOutputStream(System.out).writeObject(供应商);
}
}
然后,不会捕获周围的实例
请注意,这可以看作是lambda表达式的一般思想,将函数定义为与所编写的上下文中具有完全相同含义的表达式,但函数参数的引入除外。函数接口实例的生成只是以兼容和有用的方式将此概念引入Java编程语言的工具,而不是影响lambda表达式含义的概念。简单地说:
- 匿名类在非静态上下文中创建时始终引用封闭实例
lambda中的该
是lambda的封闭实例(而不是lambda本身)
由此可知,lambda表达式中的匿名类捕获了包含lambda表达式的实例。在您的示例中,它捕获了不可序列化的Main
实例
可能的解决办法:
在静态上下文中创建lambda:在静态方法中或分配给静态字段
将匿名类替换为静态
嵌套类:
import java.io.ObjectOutputStream;
导入java.io.Serializable;
导入java.util.function.Supplier;
公共班机{
公共静态void main(字符串[]args)引发异常{
新的Main();
}
private Main()引发异常{
供应商=(供应商和可序列化)MyThread::new;
新ObjectOutputStream(System.out).writeObject(供应商);
}
私有静态类MyThread扩展线程{
}
}
因此,这与lambda的关系不如匿名内部类和它捕获的this
表达式的意义与它们所编写的上下文中的含义完全相同
——除了返回
、中断
和继续
。例如,没有它们就排除了使用lambdas循环的一些重要用例,并限制了forEach
的实用性。@尤金:OP已经了解到new Thread(){}
将捕获一个外部this
实例,关键是它将捕获的外部this
,是Main
实例,它是lambda特定的行为。因此线程
的子类将是Main
的匿名内部类,而不是匿名的供应商
实现。由于new Thread(){}
需要捕获Main
实例,lambda表达式将是一个捕获lambda表达式。@William F。
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) MyThread::new;
new ObjectOutputStream(System.out).writeObject(supplier);
}
private static class MyThread extends Thread {
}
}