可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Are there any extensions for the Java programming language that make it possible to create nested functions? There are many situations where I need to create methods that are only used once in the context of another method or for-loop. I've been unable to accomplish this in Java so far, even though it can be done easily in Javascript.
For example, this can't be done in standard Java:
for(int i = 1; i < 100; i++){
times(2); //multiply i by 2 and print i
times(i); //square i and then print the result
public void times(int num){
i *= num;
System.out.println(i);
}
}
回答1:
Java 8 introduces lambdas.
java.util.function.BiConsumer<Integer, Integer> times = (i, num) -> {
i *= num;
System.out.println(i);
};
for (int i = 1; i < 100; i++) {
times.accept(i, 2); //multiply i by 2 and print i
times.accept(i, i); //square i and then print the result
}
The () ->
syntax works on any interface that defines exactly one method. So you can use it with Runnable
but it doesn't work with List
.
BiConsumer
is one of many functional interfaces provided by java.util.function.
It's worth noting that under the hood, this defines an anonymous class and instantiates it. times
is a reference to the instance.
回答2:
The answer below is talking about the closest you can get to having nested functions in Java before Java 8. It's not necessarily the way I'd handle the same tasks which might be handled with nested functions in Javascript. Often a private helper method will do just as well - possibly even a private helper type, which you create an instance of within the method, but which is available to all methods.
In Java 8 of course, there are lambda expressions which are a much simpler solution.
The closest you can easily come is with an anonymous inner class. That's as close as Java comes to closures at the moment, although hopefully there'll be more support in Java 8.
Anonymous inner classes have various limitations - they're obviously rather wordy compared with your Javascript example (or anything using lambdas) and their access to the enclosing environment is limited to final variables.
So to (horribly) pervert your example:
interface Foo {
void bar(int x);
}
public class Test {
public static void main(String[] args) {
// Hack to give us a mutable variable we can
// change from the closure.
final int[] mutableWrapper = { 0 };
Foo times = new Foo() {
@Override public void bar(int num) {
mutableWrapper[0] *= num;
System.out.println(mutableWrapper[0]);
}
};
for (int i = 1; i < 100; i++) {
mutableWrapper[0] = i;
times.bar(2);
i = mutableWrapper[0];
times.bar(i);
i = mutableWrapper[0];
}
}
}
Output:
2
4
10
100
Is that the output you get from the Javascript?
回答3:
I think that the closest you can get to having nested functions in Java 7 is not by using an anonymous inner class (Jon Skeet's answer above) but by using the otherwise very rarely used local classes. This way, not even the interface of the nested class is visible outside its intended scope and it's a little less wordy too.
Jon Skeet's example implemented with a local class would look as follows:
public class Test {
public static void main(String[] args) {
// Hack to give us a mutable variable we can
// change from the closure.
final int[] mutableWrapper = { 0 };
class Foo {
public void bar(int num) {
mutableWrapper[0] *= num;
System.out.println(mutableWrapper[0]);
}
};
Foo times = new Foo();
for (int i = 1; i < 100; i++) {
mutableWrapper[0] = i;
times.bar(2);
i = mutableWrapper[0];
times.bar(i);
i = mutableWrapper[0];
}
}
}
Output:
2
4
10
100
回答4:
Such methods are sometimes called closures. Have a look at Groovy – perhaps you will prefer it to Java. In Java 8 there will probably be closures as well (see JSR335 and deferred list).
回答5:
Consider making an anonymous local class and using its initializer block to do the work:
public class LocalFunctionExample {
public static void main(final String[] args) {
for (final int i[] = new int[] { 1 }; i[0] < 100; i[0]++) {
new Object() {
{
times(2); //multiply i by 2 and print i
times(i[0]); //square i and then print the result
}
public void times(final int num) {
i[0] *= num;
System.out.println(i[0]);
}
};
}
}
}
Output:
2
4
10
100
(The "final wrapper trick" is not automatically required with this technique, but was needed here to handle the mutation requirement.)
This works out to be almost as concise as the lambda version, but you get to use whatever method signatures you want, they get to have real parameter names, and the methods are called directly by their names - no need to .apply()
or whatnot. (This kind of thing sometimes makes IDE tooling work a little better too.)
回答6:
I hate to use the forbidden word but you could use a goto
statement to create an an effective subroutine inside the method. It is ugly and dangerous but much easier than what was shown in previous answers. Although the private method with a call inside of the first method is much better, and serves you needs just fine. I don't know why you would want to use a nested method for something as simple as this.