Java lambda表达式是否有不引用封闭对象的方法?

9

当使用Lambda表达式时,Java实际上创建了一个匿名(非静态)类。 非静态内部类总是包含对其封闭对象的引用。

当从另一个库中调用此Lambda表达式时,可能会在不同进程中调用Lambda表达式,此时调用将崩溃并出现找不到类的异常,因为它无法在另一个进程中找到封闭对象的类。

考虑以下示例:

public class MyClass {
    public void doSomething() {
        remoteLambdaExecutor.executeLambda(value -> value.equals("test"));
    }
}

Java会创建一个实现特定功能接口的匿名内部类,并将其作为参数传递给executeLambda()方法。然后,remoteLambdaExecutor将获取该匿名类并在远程进程中运行。 远程进程对MyClass一无所知,因此可能会抛出异常。

java.lang.ClassNotFoundException: MyClass

因为它需要MyClass来引用封闭对象。

我总是可以使用API期望的函数接口的静态实现,但这违背了本意并且没有利用lambda功能。

有没有办法使用lambda表达式来解决呢?

更新:除非将静态类以某种方式导出到其他进程中,否则我也无法使用静态类。


可能会在不同的进程中调用Lambda:听起来像个反转义。 - wero
也许Java不是这个的正确语言。也许你需要一种脚本语言。 - Ashley Frieze
1个回答

8

你最初的前提是错误的。JRE将不会生成匿名内部类。它可能会生成一个类,但如果您的lambda表达式没有访问this或类的非static成员,它就不会保留对this实例的引用。

然而,这并不意味着类本身是不必要的。由于该类托管lambda表达式的代码,因此它总是需要的。在这方面,使用static嵌套类的解决方案并没有改变任何内容,因为那时,执行代码所需的是static嵌套类。

没有办法在不传输包含要执行的代码的类的情况下将对象传输到远程执行设施(除非该类已经存在于远程站点)。


2
换句话说,Java Lambda 更像 C++ 函数指针,而不是 Lisp Lambda。Java Lambda 告诉您要运行哪些代码,但实际上并不包含该代码。我之前没有意识到这一点。 - Troy Daniels
1
好的,你需要将转移到另一个站点。由于类文件只是一堆字节,这通常是可能的。在Lambda表达式的情况下,不要关注生成的类 - 这些类可以在另一侧重新生成(这就是可序列化的Lambda的工作方式)。您只需要传输定义类即可。但是,如果您让两个进程首先访问存储的类,那么这将更容易。 - Holger
Holger,不幸的是,这并不总是在我们的控制范围内,因为它会影响整个Hazelcast机群或其他分布式网络。如果我要使用Groovy脚本,只要加载了groovy-all.jar,我就可以解决问题。它可以动态编译类。显然,lambda表达式缺乏这个功能... - ATrubka
1
Lambda表达式并不意味着改变Java语言的本质,它是一种编译语言而不是脚本语言。如果您在远程端有编译器API可用,您可以发送源代码并在另一端进行编译,但这与将类的字节码发送到另一端相比,并没有更小的工作量。因此,主要的区别不是技术问题,而是分布式网络维护者被说服部署groovy-all.jar而不是部署用于分发Java代码(或应用程序代码)的库的事实。 - Holger
1
@starwarrior8809,“基本上充当匿名类”是一个模糊的术语。为lambda表达式生成的类始终与匿名内部类共享某些特征,但即使在它们捕获“this”的情况下,仍然存在许多差异。也许这个答案可以解释一些问题。 - Holger
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接