Java:解释此代码中的Closure

时间:2013-02-09 03:18:22

标签: java closures

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

我从维基百科复制的代码:Closure

没有闭包的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支持关闭,第二个代码中哪个特殊的东西?我真的没有看到两个代码之间的主要区别。

请告诉我这一点。

谢谢:)

3 个答案:

答案 0 :(得分:4)

关键在于它们在功能上并没有那么不同:

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

等同于 Runnable 的新类实例,其具有 run()方法的等效实现。您可以使用简单的 lambda 函数替换大量的“样板”代码。

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

答案 1 :(得分:2)

区别在于:

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

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

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

但JSR-000335规范确实暗示lambdas不是匿名类。 lambda体中的thissuper具有与方法不同的含义。

规范将lambdas(在某一点上)描述为使用“合成类”实现,并声明合成类的实例可以在适当时作为编译优化重用。 (相比之下,编译器不允许对匿名类进行优化。)此优化意味着lambdas 可能的性能优于使用匿名类的等效编码。

答案 2 :(得分:0)

嗯,主要有两个不同之处。第一个链接到Javac和JVM中发生的底层进程,第二个是语法。

在给出解释之前,我们可以非常快速地定义“真实”中的封闭内容。函数式语言是:一个与其词汇环境和记忆环境相关的函数。

首先,编译器将通过扫描'来解析此代码。 ' syntaxiq context'其中将执行代码(不是变量,但是您编写的代码会导致编译器执行静态检查)。如果它发现您正在编写lambda作为等待函数接口的方法的参数,那么您将能够进行编译,以便JVM能够找到要执行的正确代码段。实际上,您将实现一个只能动态显示一个签名的界面。

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

问题中的一段代码与查看两个概念之间的区别并不是很相关,而且它并没有使用' closure'在它的整体。以下是一段有助于发现潜力的代码:

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中有一些奇怪的东西。这段代码来自online documentation,清单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 ++的指针)。根据这个原则,如果在执行堆栈中嵌入了对它的引用,则可以修改在程序的内存上下文中启动的变量。这是来自Von Neumann Architecture的隐含原则。这与功能语言不同,功能语言应该将不同的执行上下文彼此包含在一起,这种语言略有不同,并且将确保变量引用的值不会被后来的变量赋值修改。但这是另一个故事。