web-dev-qa-db-ja.com

Javaの入れ子関数

Javaネストされた関数を作成できるプログラミング言語の拡張機能はありますか?別のメソッドのコンテキストで一度だけ使用されるメソッドを作成する必要がある多くの状況があります-loop。これは、Javascriptで簡単に実行できたとしても、これまでJavaで実行できませんでした。

たとえば、これは標準の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);
    }    
}
44
Anderson Green

Java 8はラムダを導入します。

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
}

() ->構文は、厳密に1つのメソッドを定義するすべてのインターフェースで機能します。そのため、Runnableで使用できますが、Listでは機能しません。

BiConsumerは、 Java.util.function で提供される多くの機能インターフェイスの1つです。

内部で匿名クラスを定義し、インスタンス化することに注意してください。 timesはインスタンスへの参照です。

42
Neal Ehardt

以下の答えは、Javaの前にJava 8.でネストされた関数を持つことができる最も近いものについて話しています。 Javascriptのネストされた関数で処理されるタスク。多くの場合、プライベートヘルパーメソッドも同様に機能します-おそらくプライベートヘルパーtypeで、メソッド内のインスタンスを作成しますがすべてのメソッドに。

Java 8にはもちろん、はるかに簡単なソリューションであるラムダ式があります。


最も簡単に来ることができるのは、匿名の内部クラスです。 Javaはクロージャに近づいていますが、うまくいけばJava 8。

匿名の内部クラスにはさまざまな制限があります-Javascriptの例(またはラムダを使用するもの)と比べると明らかに冗長であり、囲まれた環境へのアクセスは最終変数に制限されます。

だからあなたの例を(恐ろしく)変質させるには:

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から得る出力ですか?

24
Jon Skeet

ネストされた関数を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
23
deinocheirus

このようなメソッドは、クロージャーと呼ばれることもあります。 Groovy をご覧ください。おそらくJavaよりも好まれるでしょう。 Java 8ではおそらくクロージャもあります( JSR335 および 遅延リスト を参照)。

2
dma_k

匿名ローカルクラスを作成し、その初期化ブロックを使用して作業を行うことを検討してください。

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

(「最終ラッパートリック」はこの手法では自動的に必要ではありませんが、ここでは突然変異の要件を処理するために必要でした。)

これはラムダバージョンとほぼ同じくらい簡潔になりますが、必要なメソッドシグネチャを使用することができ、実際のパラメータ名を取得し、メソッドは名前で直接呼び出されます-.apply()またはその他。 (この種のことにより、IDEツーリングも少し良くなります。)

0