web-dev-qa-db-ja.com

Javaラムダとクロージャ

ラムダが間もなくJavaお近く(J8)に来ると聞きました。いくつかのブログでそれらがどのように見えるかの例を見つけました:

SoccerService soccerService = (teamA, teamB) -> {
    SoccerResult result = null;
    if (teamA == teamB) {
        result = SoccerResult.DRAW;
    }
    else if(teamA < teamB) {
        result = SoccerResult.LOST;
    }
    else {
        result = SoccerResult.WON;
    }

    return result;
};

だからすぐに:

  • teamAteamBはどこに入力されますか?それとも(ジェネリックの奇妙な形のように)そうではありませんか?
  • ラムダはクロージャーのtypeですか、それともその逆ですか?
  • これにより、通常の匿名関数に比べてどのようなメリットがありますか?
20
IAmYourFaja

Lambda式は、ターゲットインターフェイスを実装するための単なる構文糖衣です。つまり、ラムダ式を介してインターフェイスに特定のメソッドを実装することになります。コンパイラーはインターフェース内のパラメーターのタイプを推測できるため、ラムダ式でパラメーターを明示的に定義する必要はありません。

例えば:

_Comparator<String> c = (s1, s2) -> s1.compareToIgnoreCase(s2);
_

この式では、ラムダ式は明らかに文字列のComparatorを実装しているため、これはラムダ式がcompare(String, String)を実装するためのシンタックスシュガーであることを意味します。

したがって、コンパイラーは_s1_のタイプを安全に想定でき、_s2_はStringです。

ターゲットインターフェイスタイプは、コンパイラがラムダパラメータの実際のタイプを判別するために必要なすべての情報を提供します。

Briant Goetz、Java OracleCorportionのLanguageArchitectは、JDK 8Lambdasで進行中の作業に関する記事をいくつか公開しています。あなたの質問に対する答えは次のとおりです。

この2番目の記事では、ラムダ式がバイトコードレベルでどのように実装されているかを説明し、2番目の質問の詳細を掘り下げるのに役立つ場合があります。

15
Edwin Dalorzo

その例の完全版については、 このページ を参照してください(ただし、関連する部分を以下に示します)。

タイプはSoccerServiceインターフェースとSoccerResult列挙型から推測され、スニペットには表示されません。

enum SoccerResult{
    WON, LOST, DRAW
}

interface SoccerService {
    SoccerResult getSoccerResult(Integer teamA, Integer teamB);
}

(ラムダと標準の匿名クラス)の利点は、冗長性が低下することです。

(x, y) => x + y

対次のようなもの:

new Adder()
{
  public int add(int x, int y)
  {
    return x + y;
  }
}

クロージャとラムダの違いについては、 この質問 を参照してください。

15
DNA
  • TeamAとteamBはどこに入力されますか?それとも(ジェネリックの奇妙な形のように)そうではありませんか?

Lambdaは、ジェネリックメソッド呼び出し(1.5以降)やひし形[ではない]演算子(1.7以降)によく似たターゲット型付けを使用します。大まかに言えば、結果が適用されるタイプが記述されている(または推測できる)場合、これは単一抽象メソッド(SAM)の基本タイプのタイプ、したがってメソッドパラメータータイプを提供するために使用されます。

1.5のジェネリックメソッド推論の例として:

Set<Team> noTeams = Collections.emptySet(); 

そして1.7のダイヤモンドオペレーター:

Set<Team> aTeams = new HashSet<>();

チーム、チーム、チーム、チーム、チーム、チーム。 Wordチームと言うのも大好きです。

  • ラムダは一種のクロージャーですか、それともその逆ですか?

ラムダは、匿名の内部クラスとほぼ同じ方法でクロージャの限定された形式ですが、いくつかのランダムな違いがあります。

外側のthisは内側のthisによって隠されていません。これは、ラムダと匿名内部クラスの同じテキストが微妙にしかし完全に異なることを意味する可能性があることを意味します。それは奇妙な質問でStackOverflowを忙しくし続けるはずです。

内部thisの不足を補うために、ローカル変数に直接割り当てられている場合、その値はラムダ内でアクセス可能です。 IIRC(確認できましたが、しません)、匿名の内部クラスでは、ローカルはスコープ内にあり、外部スコープ内の変数を非表示にしますが、使用することはできません。インスタンスイニシャライザーがないため、これを指定するのがはるかに簡単になると思います。

たまたまfinalとマークされていないが、マークされている可能性があるローカルフィールドは、finalであるかのように扱われます。したがって、それらは範囲内ではありませんが、実際にはそれらを読み取ることができます(書き込みはできません)。

  • これにより、通常の匿名関数に比べてどのようなメリットがありますか?

簡単な構文。それについてです。

もちろん、残りのJava構文は、これまでと同じように絶望的に悪いです。

これが最初の実装ではないと思いますが、ラムダは[内部]クラスとして実装される代わりに、メソッドハンドルを使用できます。メソッドハンドルのパフォーマンスは、以前の予測をいくらか下回っています。クラスを廃止することで、バイトコードのフットプリント、場合によっては実行時のフットプリント、そして確かにクラスのロード時間を削減できます。ほとんどの匿名の内部クラス(Serializableではなく、些細な静的イニシャライザー)が、特に顕著な非互換性なしに、よく考えられていないクラス読み込みメカニズムを通過しなかった実装が存在する可能性があります。

(用語wrthidingが正しいことを願っています。)