web-dev-qa-db-ja.com

Java 8はクロージャをサポートしますか?

よくわかりません。 Java8は石器時代から出現し、ラムダ/クロージャのサポートを開始すると考えていました。しかし、私がしようとすると:

public static void main(String[] args) {
    int number = 5;

    ObjectCallback callback = () -> {
        return (number = number + 1);
    };

    Object result = callback.Callback();
    System.out.println(result);
}

number should be effectively final。それは、私が思う閉鎖ではありません。これは、参照ではなく値によって環境をコピーしているように聞こえます。

ボーナス質問!

Android Java-8機能をサポートしますか?

43
sircodesalot

なぜそうなのか、Java。なんで?.

関連するOracle Javaチームメンバーと真の答えを得るために長い(プライベート)ディスカッションを行う必要があります。


しかし、後方互換性とプロジェクトのリソース制約の組み合わせだと思います。そして、現在のアプローチが実際的な観点から「十分」であるという事実。

プロシージャコンテキストをファーストクラスオブジェクト(つまりクロージャ)として実装するには、特定のローカル変数の有効期間が宣言メソッド呼び出しの戻りを超えて延長する必要があります。つまり、単にスタックに置くことはできません。代わりに、someローカル変数がヒープオブジェクトのフィールドでなければならない状況になります。つまり、新しい種類の隠しクラスOR JVMアーキテクチャの根本的な変更が必要です。

この種のものを実装することは技術的に可能ですが、Java言語は「グリーンフィールド」言語ではありません。「本当のクロージャ」をサポートする必要性の変化は難しいでしょう:

  • すべてのツールチェーンを更新するには、Oracleおよびサードパーティの実装者の多大な努力が必要です。 (そして、私たちは単にコンパイラについて話しているだけではありません。デバッガ、プロファイラ、難読化ツール、バイトコードエンジニアリングフレームワーク、永続化フレームワークがあります...)

  • 次に、これらの変更の一部が、数百万の既存のデプロイ済みのJavaアプリケーションの下位互換性に影響を与えるリスクがあります。

  • JVMを何らかの方法で活用するother言語などに潜在的な影響があります。たとえば、Androidは、Davlikツールチェーンの「入力言語」としてJVMアーキテクチャ/バイトコードファイルに依存します。Pythonの言語実装は、Ruby =およびJVMプラットフォーム用にコードが生成するさまざまな機能言語。


要するに、「Java=」の「本当のクロージャー」は、関係者全員にとって大きな恐ろしい提案です。「ファイナルのクロージャー」ハックは、機能する実用的な妥協であり、実際には十分です。

最後に、将来のエディションではfinal制限が削除される可能性が常にあります。 (私は息を止めませんが...)


Android Java-8機能をサポートしますか?

誰かが信頼できる内部知識を持っていない限り、答えることは不可能です。そして、もし彼らがここでそれを明らかにするのはクレイジーだろう。確かにGoogleはJava 8。

ただし、KitKatおよび対応するバージョンのJava StudioまたはEclipse ADTでAndroid 7構文拡張がサポートされるようになりました。

40
Stephen C

「クロージャ」の定義を述べる必要があります。

私にとって、「クロージャ」とは、それを囲むスコープからローカル変数をキャプチャ(「クローズ」)し、その変数を使用できるもの(関数、オブジェクト、またはメソッドを持つような何らかの方法で実行できる他のもの)です。コード内で、関数またはオブジェクトのメソッドが後で実行された場合でも、外側のスコープが存在しなくなった場合を含めて。異なる言語では、値、参照、またはその両方によって変数をキャプチャできる場合があります。

この定義により、Java無名クラス(Java 1.1)以降に存在している)areクロージャは、ローカルを参照できるためそれを囲むスコープからの変数。

Java 8のラムダは、基本的に匿名クラスの特別なケースです(つまり、メソッドを1つだけ持つインターフェイス(「機能インターフェイス」)を実装し、インスタンス変数を持たない匿名クラス、 (明示的または暗黙的にthisを使用して)自分自身を参照しないラムダは、同等の匿名クラス式に書き換えることができるため、上記のことはラムダにも適用されます。

それは、私が思う閉鎖ではありません。

まあ、あなたはごめんなさい、「閉鎖」の台無しにされた定義を持っています。

13
newacct

final制限には技術的な理由があると思います。ラムダ式は、参照がスタック上に存在し、メソッドの終了後も存続しないため、周囲のメソッドコンテキストから値を取得するだけです。

コンテキストの値intoを参照に設定すると、「実際の」クロージャーを構築できます。

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

オースガベ:

Ich esse 1 Kuchen.
Ich esse 2 Kuchen.
Ich esse 3 Kuchen.

配列の代わりに、任意のオブジェクトの適切なインスタンスを取ることができます。これは、オブジェクトがヒープ上に存在し、このインスタンスへの参照のみがラムダ式で保持されるためです。

この場合、containerの値はmutterfunktionに囲まれます。 mutterfunktionを呼び出すたびに、新しい参照インスタンスが作成されます。

値は関数の外部からアクセスできません(Java 7およびそれ以前)に組み込むのは非常に困難でした。ラムダ式はメソッド参照として実装されるため、これに関係する内部クラスはありません。例。

メソッドのコンテキストでcontainerを定義して、ラムダの外側で変更を行うこともできます。

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

オースガベ:

Ich esse 1 Kuchen.
Ich esse 2 Kuchen.
Ich esse 4 Kuchen.

したがって、あなたの質問に対する答えは「はい」になります。

11
Volker Seibt

最終参照を使用して、外部スコープで宣言された変数の状態の変更を回避できますが、結果は同じままで、クロージャー外部スコープの状態は保持されず、参照されるオブジェクトへのさらなる変更は(最終参照によって)閉鎖で見られる。

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

それでも楽しい...

1
DrGranit