50

我很困惑。我认为 Java 8 将从石器时代出现并开始支持 lambdas/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 功能吗?

4

5 回答 5

43

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

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


但我怀疑这是向后兼容性和项目资源限制的结合。从务实的角度来看,当前的方法“足够好”这一事实。

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

虽然在技术上实现这种东西是可行的,但 Java 语言并不是一种“绿地”语言。在 Java 中支持“真正的闭包”所需的性质改变将是困难的:

  • Oracle 和第 3 方实施者需要付出巨大努力才能更新所有工具链。(而且我们不仅仅是在谈论编译器。还有调试器、分析器、混淆器、字节码工程框架、持久性框架……)

  • 然后,其中一些更改可能会影响数百万现有已部署 Java 应用程序的向后兼容性。

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


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

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


android 会支持 Java-8 功能吗?

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

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

于 2013-06-20T03:05:28.100 回答
14

您必须说明您对“关闭”的定义。

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

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

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

那是呃,我认为不是关闭。

好吧,先生,您对“关闭”的定义搞砸了。

于 2013-06-21T00:52:19.120 回答
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());
    }
}

奥斯加贝:

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());
}

奥斯加贝:

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

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

于 2014-10-09T11:55:17.267 回答
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;
}

但还是很有趣...

于 2014-02-13T17:21:06.003 回答
-1

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

于 2021-02-27T20:54:38.647 回答