Java 8 是否支持闭包?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17204279/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-01 01:18:41  来源:igfitidea点击:

Does Java 8 Support Closures?

javalambdaclosuresjava-8

提问by sircodesalot

I'm confused. I thought Java 8 was going to emerge from the stone age and start supporting lambdas/closures. But when I try this:

我糊涂了。我认为 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);
}

...it says that number should be effectively final. That's uh, not a closure I think. That just sounds like it's copying the environment by value, rather than by reference.

……是这么说的number should be effectively final。那是呃,我认为不是结束。这听起来像是按值复制环境,而不是按引用。

Bonus question!

奖金问题!

Will Android support Java 8 features?

Android 会支持 Java 8 功能吗?

采纳答案by Stephen C

Why oh why, Java. Why oh why.

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

You would need to hold a long (private) discussion with the relevant Oracle Java team members for the true answer. (If they would be willing to talk to you ...)

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



But I suspect it is a combination of backwards compatibility and project resourcing constraints. And the fact that the current approach is "good enough" from a pragmatic perspective.

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

Implementing procedure contexts as first-class objects (i.e. closures) requires that the lifetime of certain local variables extends beyond the return of the declaring method call. That means that you cannot just put them on the stack. Instead you end up with a situation where somelocal variables have to be fields of an heap object. That means you need a new kind of hidden class OR fundamental changes to the JVM architecture.

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

While it is technically possible to implement this kind of thing, the Java language is not a "green field" language. A change of the nature that would needed to support "real closures" in Java would be difficult:

虽然在技术上可以实现这种事情,但 Java 语言并不是“绿色领域”语言。在 Java 中支持“真正的闭包”所需的性质的改变将是困难的:

  • It would take a huge amount of effort from Oracle and 3rd party implementors to update all of the tool chains. (And we are not just talking about compilers. There are debuggers, profilers, obfuscators, bytecode engineering frameworks, persistence frameworks ...)

  • Then there is the risk that some of these changes would impact on backwards compatibility for the millions of existing deployed Java applications out there.

  • There is the potential impact on otherlanguages, etc that leverage the JVM in some way. For example, Android depends on the JVM architecture / bytecode files as the "input language" for its Davlik tool-chain. There are language implementations for Python, Ruby and various functional languages that code generate for the JVM platform.

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

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

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



In short "real closures" in Java would be a big scary proposition for everyone concerned. The "closures for finals" hack is a pragmatic compromise that does work, and that is good enough in practice.

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

Lastly, there is always the possibility that the finalrestriction could be removed in a future edition. (I wouldn't hold my breath though ....)

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



Will android support Java-8 features?

android 会支持 Java-8 特性吗?

That is impossible to answer unless someone has credible inside knowledge. And if they did, they would be crazy to reveal it here. Certainly Google have not announced support for Java 8.

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

But the good news is that Java 7 syntax extensions are now supported with KitKat and corresponding versions of Android Studio or Eclipse ADT.

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

回答by newacct

You will have to state your definition of "closure".

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

To me, a "closure" is something (a function or object or other thing that can be run in some way like having methods) that captures ("closes over") a local variable from its enclosing scope, and which can use that variable in its code, even when the function or object's method is run at a later time, including when the enclosing scope no longer exists. In different languages, it may be possible to capture a variable by value, or by reference, or both.

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

By this definition, Java anonymous classes (which have been there since Java 1.1) areclosures, since they can refer to local variables from its enclosing scope.

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

Lambdas in Java 8 are basically a special case of anonymous classes (namely, an anonymous class which implements an interface with exactly one method (a "functional interface"), and which has no instance variables, and which does not refer to itself (using thisexplicitly or implicitly)). Any lambda can be re-written into an equivalent anonymous class expression. So what is said above also apply to lambdas.

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

That's uh, not a closure I think.

那是呃,我认为不是结束。

Well, you sir, have a messed up definition of "closure".

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

回答by Volker Seibt

I think the finalrestriction has technical reasons. A lambda expression simply takes the value from the surrounding method context because the reference lives on the stack and will not survive the finishing of the method.

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

If you put the context's value intoa reference, you can build a "real" closure:

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

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.

Instead of an array you can take any suitable instance of any object, because it lives on the heap and only the reference to this instance is kept (final) in the lambda expression.

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

In this case the value of containeris enclosed into mutterfunktion. Every call to mutterfunktioncreates a new referenced instance.

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

The value is not accessible from outside the function (which was very hard to build in Java 7 and before). Because of lambda expressions are implemented as method references, there are no inner classes involved in this example.

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

You could also define containerin the method's context an you will be able to do changes outside the 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.

So the answer to your question will be "yes".

所以你的问题的答案是“是”。

回答by DrGranit

You can use final references to get around altering state of variables declared in an outer scope, but the result remains the same, the state of the closures outer scope is not kept and further alterations to the object referenced to (by a final reference) are seen in the closure.

您可以使用最终引用来绕过更改在外部作用域中声明的变量的状态,但结果保持不变,不保留闭包外部作用域的状态,并且对(通过最终引用)引用的对象的进一步更改是在闭包中看到。

@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;
}

but still fun...

但还是很有趣...