私はJava7ラムダ提案のプロセスと進化をたどっていません。最新の提案の文言が何であるかさえわかりません。これを真実の陳述ではなく、暴言/意見と考えてください。また、私は何年もの間Javaを使用していないので、構文が錆びていて、場所によっては正しくない可能性があります。
まず、Java言語のラムダとは何ですか?シンタックスシュガー。一般に、ラムダを使用すると、コードで小さな関数オブジェクトを適切に作成できますが、そのサポートは、内部クラスを使用することで、Java言語ですでにある程度事前設定されています。
では、ラムダの構文はどれほど優れているのでしょうか?以前の言語構成をどこで上回っていますか?どこがいいのでしょうか?
手始めに、ラムダ関数に使用できる構文が2つあるという事実は嫌いです(ただし、これはC#の行に含まれるため、私の意見は広まっていないと思います。シュガーコートが必要な
#(int x)(x*x)
場合は#(int x){ return x*x; }
、二重構文は他に何も追加しません。私は2番目の構文を好みました。これは、書き込みの追加コストと短いバージョンでより一般的return
です
;
。
本当に便利なように、ラムダは変数が定義されているスコープとクロージャから変数を取得できます。内部クラスと一貫性があるため、ラムダは「効果的に最終的な」変数のキャプチャに制限されます。言語の以前の機能との一貫性は素晴らしい機能ですが、甘さのために、再割り当てできる変数をキャプチャできると便利です。その目的のために、彼らは、コンテキストに存在し、注釈が付けられた変数が参照によって@Shared
キャプチャされることを考慮しています、割り当てを許可します。ラムダが変数を使用する方法は、ラムダが定義されている場所ではなく、変数の宣言の場所で決定されるため、これは奇妙に思えます。1つの変数を複数のラムダで使用できます。これにより、すべてのラムダで同じ動作が強制されます。
ラムダは実際の関数オブジェクトをシミュレートしようとしますが、提案は完全には実現しません。これまで、識別子は一貫性が保たれているオブジェクトまたはメソッドのいずれかを示し、ラムダを呼び出すには!
後を使用する必要があるため、パーサーを単純に保つためです。ラムダ名:#(int x)(x*x)!(5)
を返し25
ます。これにより、言語の他の部分とは異なるラムダに使用する新しい構文がもたらされます。これは
、仮想ジェネリックインターフェイスの同義語として何とか!
立っていますが、完全にしないのはなぜですか?.execute
Lambda<Result,Args...>
新しい汎用(仮想)インターフェイスLambda
を作成できます。インターフェイスは実際のインターフェイスではなく、そのようなファミリであるため、仮想である必要があります:、、
Lambda<Return>
...単一の実行メソッドを定義できます。これは、C ++のようになりたいのですが、それが負担になる場合は、メソッド実行のショートカットとしてを使用して、他の名前でもかまいません。Lambda<Return,Arg1>
Lambda<Return,Arg1,Arg2>
operator()
!
interface Lambda<R> {
R exec();
}
interface Lambda<R,A> {
R exec( A a );
}
identifier!(args)
次に、コンパイラはに変換する
だけidentifier.exec( args )
で済みます。これは簡単です。ラムダ構文の変換では、コンパイラーが実装されている適切なインターフェースを識別する必要があり、次のように照合できます。
#( int x )(x *x)
// translated to
new Lambda<int,int>{ int exec( int x ) { return x*x; } }
これにより、ユーザーは、より複雑な状況でラムダとして使用できる内部クラスを定義することもできます。たとえば、ラムダ関数が@Shared
読み取り専用の方法で注釈が付けられた変数をキャプチャする必要がある場合、またはキャプチャされたオブジェクトの状態をキャプチャの場所で維持する必要がある場合、Lambdaの手動実装が利用可能になります。
new Lambda<int,int>{ int value = context_value;
int exec( int x ) { return x * context_value; }
};
現在の内部クラスの定義と同様の方法で、したがって現在のJavaユーザーにとって自然な方法です。これは、たとえば、乗数ラムダを生成するためのループで使用できます。
Lambda<int,int> array[10] = new Lambda<int,int>[10]();
for (int i = 0; i < 10; ++i ) {
array[i] = new Lambda<int,int>{ final int multiplier = i;
int exec( int x ) { return x * multiplier; }
};
}
// note this is disallowed in the current proposal, as `i` is
// not effectively final and as such cannot be 'captured'. Also
// if `i` was marked @Shared, then all the lambdas would share
// the same `i` as the loop and thus would produce the same
// result: multiply by 10 --probably quite unexpectedly.
//
// I am aware that this can be rewritten as:
// for (int ii = 0; ii < 10; ++ii ) { final int i = ii; ...
//
// but that is not simplifying the system, just pushing the
// complexity outside of the lambda.
これにより、ラムダとラムダを受け入れるメソッドの両方を新しい単純な構文で使用できるようになります。#(int x){ return x*x; }
または、砂糖コーティングが意図したセマンティクスに干渉する特定の場合のより複雑な手動アプローチを使用できます。
全体として、ラムダの提案はさまざまな方向に改善できると思います。シンタックスシュガーを追加する方法は、リークのある抽象化であり(ラムダに固有の問題を外部で処理しています)、下位レベルのインターフェイスを提供しないことで、単純なユースケースに完全に適合しないユースケースでは、ユーザーコードが読みにくくなります。: