开发者

Nested functions in Java

开发者 https://www.devze.com 2023-04-03 17:27 出处:网络
Are there any extensions for the Java programming language that make it possible to create nested functions?

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 a开发者_JS百科re 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);
    }
}


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.


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 code?


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), 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


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).


For non-argument method you can create Runnable object

private static void methodInsideMethod(){
    Runnable runnable = new Runnable(){
        @Override
        public void run(){
            System.out.println("Execute something");
        }
    };

    for(int i = 0; i < 10; i++){
        runnable.run();
    }
}


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.)


I don't know if anyone else has figured this out, but apparently you can do magic with the var keyword that comes with Java 10 and above. This saves you from having to declare an interface if you don't have one that will work for you.

public class MyClass {
    public static void main(String args[]) {
        var magic = new Object(){
            public void magic(){
                System.out.println("Hello World!");
            }
        };
        magic.magic();
    }
}

I have tested it and it works. I haven't found a Java compiler that lets you do a simple share yet.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消