8

我理解闭包,并且已经应用​​了一些语言,例如 Python 和 SML。然而,当我阅读关于 Java 闭包的维基百科(当然,只有 8 个版本)时,我不明白 Java 在他们的示例中是否支持闭包的区别。

我从维基百科复制的那些代码:关闭

没有闭包的java代码:

class CalculationWindow extends JFrame {
  private volatile int result;
  ...
  public void calculateInSeparateThread(final URI uri) {
    // The expression "new Runnable() { ... }" is an anonymous class implementing the 'Runnable' interface.
    new Thread(
      new Runnable() {
        void run() {
          // It can read final local variables:
          calculate(uri);
          // It can access private fields of the enclosing class:
          result = result + 10;
        }
      }
    ).start();
  }
}

如果 Java 支持闭包,代码将如下所示:

class CalculationWindow extends JFrame {
private volatile int result;
  ...
  public void calculateInSeparateThread(final URI uri) {
    // the code () -> { /* code */ } is a closure
    new Thread(() -> {
        calculate(uri);
        result = result + 10;
    }).start();
  }
}

所以,我的问题是:如果 Java 支持闭包,那么第二个代码中有哪些特别之处?我真的看不出两个代码之间的主要区别。

请告诉我这一点。

谢谢 :)

4

3 回答 3

4

关键是它们在功能上并没有太大的不同:

() -> {
    calculate(uri);
    result = result + 10;
}

等效于具有run()方法的等效实现的Runnable的新类实例。你用一个简单的lambda函数替换了很多“样板”代码。

使用 lambda,您的代码变得更具表现力、简洁、更易于编写和可读性。一旦你进入闭包和 lambda 函数的世界,这就是好处开始的地方。

于 2013-02-09T04:01:35.547 回答
2

不同之处在于:

  • 在第一种情况下,您要声明一个匿名类并创建一个实例,但是
  • 在第二种情况下,没有类也没有实例。相反,有一个 lambda ... 实际上是一个匿名函数。

它们达到了相同的目的,但 lambda 语法肯定更轻量级。

final但是,如果 lambda 是(或实际上是最终的),则 lambda 只能访问声明它的范围内的局部变量;请参阅 JSR-000335 审查草案 #2 中的 15.27.2。所以你可能会说 Java lambdas 不是完全闭包。

但是 JSR-000335 规范确实暗示 lambda 不是匿名类。并且在 lambda 主体中thissuper它们在方法中具有不同的含义。

该规范将 lambdas(在某一时刻)描述为使用“合成类”实现,并声明合成类的实例可以在适当的时候作为编译优化被重用。(相比之下,不允许编译器对匿名类进行优化。)这种优化意味着 lambda可能比使用匿名类编码的等效代码执行得更好。

于 2013-02-09T03:59:55.063 回答
0

嗯,主要有两个区别。第一个与 Javac 和 JVM 中发生的底层进程相关联,第二个是语法。

在给出解释之前,让我们快速定义一下“真正的”函数式语言中的闭包是什么:一个函数及其词法环境和内存上下文。

首先,编译器将通过“扫描”将在其中执行代码的“语法上下文”来解析此代码(不是变量,而是您编写的代码导致编译器进行静态检查)。如果它发现您正在编写一个 lambda 作为等待功能接口的方法的参数,您将能够进行编译,以便 JVM 能够找到要执行的正确代码段。事实上,您将要实现一个只动态公开一个签名的接口。

然后,语法本身很重要,因为它不会为您提供与匿名类相同的功能,您可以在同一个地方实现或覆盖多个方法。

问题中的那段代码与查看这两个概念之间的区别并不是很相关,并且它整体上没有使用“闭包”。这是一段代码,可以帮助您了解潜力:

public class MyClass
{
    // A one method interface.
    public static interface Function
    {
        void execute(int a);
    };

    // An implementation of Function using the closure and lambda.
    public static Function createPrintAddToTenFunction()
    {
        Integer x = 10;
        Function func = (a) -> System.out.println(x + a);
        return func;
    }

    // A basic program.
    public static void main(String args[])
    {
        Function func = createPrintAddToTenFunction();
        func.execute(10);
    }
}

这在 java 8 中编译并将打印: 20

但是,Java 中有一些奇怪的闭包。这段代码来自在线文档,清单 14:

public static void main(String... args) {
  StringBuilder message = new StringBuilder();
  Runnable r = () -> System.out.println(message);
  message.append("Howdy, ");
  message.append("world!");
  r.run();
}

这将打印 Howdy, world!

而这正是函数式编程语言所不想要的。JVM 这样做是因为 Java 是一种面向对象的语言并且非常擅长它。

在 Java 中,您使用大量对堆的引用(这些引用接近于 C 或更接近的 C++ 等语言中的指针)。使用此原则,如果您在执行堆栈中嵌入了对该变量的引用,则可以修改在程序的内存上下文中启动的变量。这是来自冯诺依曼架构的隐含原则。这与函数式语言不同,后者应该将不同的执行上下文包含在彼此之间,后者略有不同,并将确保变量引用的值不会被以后的变量赋值修改。但那是另一回事了。

于 2018-02-28T22:23:12.627 回答