Java中的嵌套函数

时间:2011-09-09 21:36:53

标签: java function nested

Java编程语言是否有任何扩展可以创建嵌套函数?在许多情况下,我需要创建仅在另一个方法或for循环的上下文中使用一次的方法。到目前为止,我无法用Java实现这一点,即使它可以在Javascript中轻松完成。

例如,这不能在标准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);
    }    
}

8 个答案:

答案 0 :(得分:39)

Java 8引入了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
}

() ->语法适用于任何只定义一个方法的接口。因此,您可以将其与Runnable一起使用,但它不适用于List

BiConsumerjava.util.function提供的众多功能接口之一。

值得注意的是,在引擎盖下,这定义了一个匿名类并实例化它。 times是对实例的引用。

答案 1 :(得分:24)

下面的答案是谈论在Java 8之前在Java中嵌套函数最接近的问题。这不一定是我处理Javascript中嵌套函数可能处理的相同任务的方式。通常私有帮助器方法也可以做 - 甚至可能是私有帮助器类型,您可以在方法中创建一个实例,但是所有方法都可以使用它。

在Java 8中,当然有lambda表达式是一个更简单的解决方案。


你最容易接近的是一个匿名的内部阶级。这与Java目前的关闭一样接近,尽管希望Java 8中会有更多的支持。

匿名内部类有各种限制 - 与你的Javascript示例(或使用lambdas的任何东西)相比,它们显然相当冗长,并且它们对封闭环境的访问仅限于最终变量。

所以(可怕地)歪曲你的榜样:

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

输出:

2
4
10
100

这是你从Javascript获得的输出吗?

答案 2 :(得分:22)

我认为在Java 7中最接近嵌套函数的方法不是使用匿名内部类(Jon Skeet上面的答案),而是使用其他很少使用的本地类。这样,即使嵌套类的接口在其预期范围之外也是可见的,并且它也不那么冗长。

使用本地类实现的Jon Skeet示例如下所示:

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

输出:

2
4
10
100

答案 3 :(得分:2)

这种方法有时被称为闭包。看看Groovy - 也许您会更喜欢Java。在Java 8中,可能还会有闭包(请参阅JSR335deferred list)。

答案 4 :(得分:0)

考虑创建一个匿名本地类并使用其初始化块来完成工作:

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

输出:

2
4
10
100

(这种技术不会自动要求“最终包装技巧”,但这里需要处理突变要求。)

这几乎和lambda版本一样简洁,但你可以使用你想要的任何方法签名,它们可以拥有真正的参数名称,并且这些方法可以直接通过它们的名称来调用 - 不需要{{ 1}}或者诸如此类的东西。 (这种事情有时会让IDE工具的工作变得更好。)

答案 5 :(得分:0)

对于非参数方法,您可以创建 Runnable 对象

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

答案 6 :(得分:0)

我不知道是否有其他人发现了这一点,但显然您可以使用 var 关键字来施展魔法。如果您没有适合您的接口,这将使您不必声明接口。

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

我已经对其进行了测试并且可以正常工作。我还没有找到可以让您进行简单共享的 Java 编译器。

答案 7 :(得分:-5)

我讨厌使用禁用词但您可以使用goto语句在方法内创建一个有效的子例程。这是丑陋和危险的,但比以前的答案显示的要容易得多。虽然在第一种方法中调用私有方法要好得多,并且服务你需要很好。我不知道为什么你会想要使用嵌套方法来做这么简单的事情。