可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm confused. I thought Java8 was going to emerge from the stone age and start supporting lambdas / closures. But when I try:
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.
Bonus question!
Will android support Java-8 features?
回答1:
Why oh why, Java. Why oh why.
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 ...)
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 some local 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.
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 need to support "real closures" would be difficult:
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 other languages, 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.
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.
Lastly, there is always the possibility that the final
restriction could be removed in a future edition. (I wouldn't hold my breath though ....)
Will android support Java-8 features?
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.
But the good news is that Java 7 syntax extensions are now supported with KitKat and corresponding versions of Android Studio or Eclipse ADT.
回答2:
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) are closures, since they can refer to local variables from its enclosing scope.
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 this
explicitly or implicitly)). Any lambda can be re-written into an equivalent anonymous class expression. So what is said above also apply to lambdas.
That's uh, not a closure I think.
Well, you sir, have a messed up definition of "closure".
回答3:
I think the final
restriction 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.
If you put the context's value into a 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.
In this case the value of container
is enclosed into mutterfunktion
. Every call to mutterfunktion
creates a new referenced instance.
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.
You could also define container
in the method's context an you will be able to do changes outside the 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".
回答4:
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...