Java 8是否支持闭包?

时间:2013-06-20 02:15:29

标签: java lambda closures java-8

我很困惑。我认为Java8将从石器时代开始出现并开始支持lambda / closures。但是当我尝试:

public static void main(String[] args) {
    int number = 5;

    ObjectCallback callback = () -> {
        return (number = number + 1);
    };

    Object result = callback.Callback();
    System.out.println(result);
}

它说number should be effectively final。那是呃,不是我认为的封闭。这听起来像是按价值复制环境,而不是通过参考。

加分问题!

android会支持Java-8功能吗?

5 个答案:

答案 0 :(得分:40)

  

为什么哦为什么,Java。为什么哦为什么。

您需要与相关的Oracle Java团队成员进行长时间(私人)讨论,以获得真正的答案。 (如果他们愿意和你谈谈......)


但我怀疑它是向后兼容性和项目资源约束的组合。事实上,从务实的角度来看,目前的方法“足够好”。

将过程上下文实现为第一类对象(即闭包)要求某些局部变量的生命周期超出声明方法调用的返回值。这意味着你不能只把它们放在堆栈上。相反,您最终会遇到某些局部变量必须是堆对象的字段的情况。这意味着您需要一种新的隐藏类或对JVM体系结构的基本更改。

虽然技术上可以实现这种功能,但Java语言不是“绿色领域”语言。改变支持“真正关闭”的性质需要很难:

  • Oracle和第三方实现者需要花费大量精力来更新所有工具链。 (我们不只是讨论编译器。有调试器,分析器,混淆器,字节码工程框架,持久性框架......)

  • 然后存在这些变化中的一些会影响数百万现有已部署的Java应用程序的向后兼容性的风险。

  • 以某种方式利用JVM对其他语言等产生潜在影响。例如,Android依赖于JVM架构/字节码文件作为其Davlik工具链的“输入语言”。有Python,Ruby和各种函数语言的语言实现,这些语言为JVM平台生成代码。


简而言之,Java中的“真正关闭”对所有相关人员来说都是一个非常可怕的命题。 “决赛关闭”黑客攻击是一种务实的妥协,确实有效,而且在实践中已经足够好了。

最后,在将来的版本中可能会删除final限制。 (虽然我不会屏住呼吸......)


  

android会支持Java-8功能吗?

除非有可靠的内部知识,否则无法回答。如果他们这样做,他们会疯狂地在这里透露它。当然谷歌还没有宣布支持Java 8。

但好消息是KitKat及相应版本的Android Studio或Eclipse ADT现在支持Java 7语法扩展。

答案 1 :(得分:13)

你必须陈述你对“封闭”的定义。

对我来说,“闭包”是某种东西(一种能够以某种方式运行的函数或对象或其他东西,比如有方法)从其封闭范围捕获(“封闭”)局部变量,并且可以即使函数或对象的方法稍后运行,包括当封闭范围不再存在时,也要在其代码中使用该变量。在不同的语言中,可以通过值或引用或两者来捕获变量。

根据这个定义,Java匿名类(自Java 1.1以来一直存在)闭包,因为它们可以从其封闭范围引用局部变量。

Java 8中的Lambda基本上是匿名类的一个特例(即,一个匿名类,它实现了一个只有一个方法的接口(一个“功能接口”),并且没有实例变量,并且没有引用本身(明确或隐含地使用this))。任何lambda都可以重写为等效的匿名类表达式。所以上面所说的也适用于lambdas。

  

那是呃,不是我认为的封闭。

嗯,你先生,对“封闭”有一个混乱的定义。

答案 2 :(得分:11)

我认为final限制有技术原因。 lambda表达式只是从周围的方法上下文中获取值,因为引用存在于堆栈中,并且不会在方法完成后继续存在。

如果您将上下文的值放入引用,则可以构建“真正的”闭包:

import java.util.function.Supplier;

public class CreatingAClosure {

    public static void main(String[] args) {
        Supplier<Supplier<String>> mutterfunktion = () -> {
            int container[] = {0};
            return () -> {
                container[0]++;
                return "Ich esse " + container[0] + " Kuchen.";
            };
        };
        Supplier<String> essen = mutterfunktion.get();
        System.out.println(essen.get());
        System.out.println(essen.get());
        System.out.println(essen.get());
    }
}

Ausgabe:

Ich esse 1 Kuchen.
Ich esse 2 Kuchen.
Ich esse 3 Kuchen.

您可以使用任何对象的任何合适的实例来代替数组,因为它存在于堆上,并且只在lambda表达式中保留(最终)对此实例的引用。

在这种情况下,container的值会包含在mutterfunktion中。每次调用mutterfunktion都会创建一个新引用的实例。

无法从函数外部访问该值(在Java 7及之前很难构建)。由于lambda表达式是作为方法引用实现的,因此本示例中没有涉及内部类。

您还可以在方法的上下文中定义container,您可以在lambda之外进行更改:

public static void main(String[] args) {
    int container[] = {0};
    Supplier<String> essen = () -> {
        container[0]++;
        return "Ich esse " + container[0] + " Kuchen.";
    };
    System.out.println(essen.get());
    System.out.println(essen.get());
    container[0]++;
    System.out.println(essen.get());
}

Ausgabe:

Ich esse 1 Kuchen.
Ich esse 2 Kuchen.
Ich esse 4 Kuchen.

因此,您的问题的答案将是“是”。

答案 3 :(得分:1)

您可以使用最终引用来改变在外部作用域中声明的变量状态,但结果保持不变,不保留闭包外部作用域的状态以及对引用的对象的进一步更改(通过最终参考文献见封闭。

@Test
public void clojureStateSnapshotTest() {
    Function wrapperFunc;
    wrapperFunc = (a) -> {
        // final reference
        final WrapLong outerScopeState = new WrapLong();

        outerScopeState.aLong = System.currentTimeMillis();
        System.out.println("outer scope state BEFORE: " + outerScopeState.aLong);

        Function closure = (b) -> {
            System.out.println("closure: " + outerScopeState.aLong);
            return b;
        };

        outerScopeState.aLong = System.currentTimeMillis();
        System.out.println("outer scope state AFTER: " + outerScopeState.aLong);

        // show correct snapshot state
        closure.apply(new Object());

        return a;
    };
    // init clojure
    wrapperFunc.apply(new Object());
}

public class WrapLong {
    public long aLong = 0;
}

但仍然很有趣......

答案 4 :(得分:-1)

闭包被认为是不满足函数式编程中纯函数限制的副作用