構文の説明
ラムダの表現は、次の部分で構成されています。
1。括弧内の正式なパラメーターのコンマ分離リスト。 CheckPerson.Testメソッドには、Personクラスのインスタンスを表すパラメーターPが含まれています。注:ラムダ式のパラメーターの種類は省略できます。さらに、パラメーターが1つしかない場合、ブラケットも省略できます。たとえば、前のセクションで説明したコード:
p-> p.getGender()== person.sex.male && p.getage()> = 18 && p.getage()<= 25
2。矢印記号: - >。パラメーターと関数体を分離するために使用されます。
3。機能本体。コードの式またはブロックで構成されています。前のセクションの例では、この式が使用されました。
p.getgender()== person.sex.male && p.getage()> = 18 && p.getage()<= 25
式を使用している場合、Javaランタイムは式の値を計算して返します。さらに、コードブロックでreturnステートメントを使用することもできます。
p-> {return p.getGender()== person.sex.male && p.getage()> = 18 && p.getage()<= 25; }ただし、リターンステートメントは式ではありません。ラムダの表現では、声明は巻き毛の装具に囲む必要がありますが、空の返品値でメソッドを呼び出すだけで、巻き毛のブレースに声明を囲む必要はないため、次の書き込み方法も正しいです。
電子メール - > System.out.println(電子メール)
ラムダの表現と方法の宣言には多くの類似点があります。したがって、ラムダ式は、匿名の方法、つまり名前定義のない方法と見なすこともできます。
上記のLambda式はすべて、1つのパラメーターのみを正式なパラメーターとして使用する式です。次の例クラスであるCaulatorは、複数のパラメーターを正式なパラメーターとして使用する方法を示しています。
パッケージcom.zhyea.zytools; public class calculator {interface integermath {int operation(int a、int b); } public int operationBinary(int a、int b、integermath op){return op.operation(a、b); } public static void main(string ... args){calculator myapp = new calculator(); integermath添加=(a、b) - > a + b; System.out.println( "40 + 2 =" + myApp.OperateBinary(40、2、添加)); System.out.println( "20-10 =" + myApp.OperateBinary(20、10、下位)); }}コードのOperatingBinaryメソッドは、2つの整数パラメーターを使用して算術演算を実行します。ここでの算術操作自体は、Integermathインターフェイスの例です。上記のプログラムでは、2つの算術演算がラムダ式:追加と減算を使用して定義されています。プログラムを実行すると、次のコンテンツが印刷されます。
40 + 2 = 4220-10 = 10
外部クラスのローカル変数にアクセスします
ローカルクラスや匿名クラスと同様に、Lambda式は、外部クラスのローカル変数にもアクセスできます。違いは、Lambda式を使用するときにオーバーライドするなどの問題を考慮する必要がないことです。ラムダの表現は単なる語彙的概念です。つまり、スーパークラスからの名前を継承する必要はなく、新しいスコープを導入する必要もありません。つまり、ラムダ表現の宣言は、その外部環境の宣言と同じ意味を持っています。これは、次の例で実証されています。
パッケージcom.zhyea.zytools; import java.util.function.consumer; public class lambdascopetest {public int x = 0;クラスFirstLevel {public int x = 1; void methodinfirstlevel(int x){//次のステートメントにより、コンパイラは「ラムダ式から参照されるローカル変数が最終的または効果的に最終的でなければならない」// x = 99でなければならないエラーを報告します。 Consumer <Integer> myConsumer =(y) - > {system.out.println( "x =" + x); //ステートメントa System.out.println( "y =" + y); System.out.println( "this.x =" + this.x); system.out.println( "lambdascopetest.this.x =" + lambdascopetest.this.x); }; myconsumer.accept(x); }} public static void main(string ... args){lambdascopetest st = new lambdascopetest(); lambdascopetest.firstlevel fl = st.new firstlevel(); fl.MethodinFirstLevel(23); }}このコードは以下を出力します。
x = 23y = 23this.x = 1lambdascopetest.this.x = 0
例のラムダ式式myConsumerのパラメーターyをxに置き換えると、コンパイラはエラーを報告します。
Consumer <Integer> myConsumer =(x) - > {// ....};コンパイラエラーメッセージは次のとおりです。「可変xは、メソッドMethodInfirstLevel(int)で既に定義されています」です。つまり、可変xはメソッドMethodInfirstlevelで定義されています。ラムダの式が新しい範囲を導入しないため、エラーが報告されます。したがって、ラムダ式の外部クラスのドメインフィールド、方法、および正式なパラメーターに直接アクセスできます。この例では、Lambda Expression myConsumerがメソッドMethodInfirstLevelのパラメーターxに直接アクセスします。外部クラスのメンバーにアクセスするとき、このキーワードは直接使用されます。この例では、this.xはfirstlevel.xを指します。
ただし、ローカルまたは匿名のクラスと同様に、ラムダ式は、最終的な(または最終に相当)宣言されたローカル変数または外部メンバーのみにアクセスできます。たとえば、サンプルコードMethodInfirstLevelメソッドの「x = 99」の前にコメントを削除します。
//次のステートメントにより、コンパイラはエラーを報告します。 Consumer <Integer> myConsumer =(y) - > {system.out.println( "x =" + x); //ステートメントa System.out.println( "y =" + y); System.out.println( "this.x =" + this.x); system.out.println( "lambdascopetest.this.x =" + lambdascopetest.this.x); };このステートメントではパラメーターxの値が変更されているため、MethodInfirstLevelのパラメーターxは最終と見なすことができません。したがって、Javaコンパイラは、ラムダ式がローカル変数xにアクセスする場合、「ラムダ式から参照されるローカル変数が最終的または効果的に最終的なものでなければならない」などのエラーを報告します。
ターゲットタイプ
ラムダ発現のタイプを決定する方法は?適切な年齢の軍人をフィルタリングするためのコードを見てみましょう。
p-> p.getGender()== person.sex.male && p.getage()> = 18 && p.getage()<= 25
このコードは2つの場所で使用されています。
public static void printpersons(list <person>名簿、チェックパーソンテスター) - ソリューション3
public void printpersonsswithspredicate(list <person>名簿、述語<パーソン>テスター) - 計画VI
PrintPersonsメソッドを呼び出すとき、この方法はチェックパーソンタイプのパラメーターを期待します。現時点では、上記の式はチェックパーソンタイプの式です。 PrintPersonsSwithSpredicateメソッドを呼び出すと、型Presticate <Person>のパラメーターが予想されます。現時点では、同じ式が型<sers <人>のタイプです。このように、メソッドで予想されるタイプによって決定されるタイプは、ターゲットタイプと呼ばれます(実際、Scalaのタイプの推論はここでより適切だと思います)。 Javaコンパイラは、ターゲットタイプのコンテキストまたはラムダの発現を発見したときの位置を通じてラムダ式のタイプを決定します。これは、Lambda式を使用することができることを意味します。これは、Javaコンパイラがタイプを推測できる場合にのみ使用できます。
ターゲットタイプとメソッドパラメーター
メソッドパラメーターの場合、Javaコンパイラは、2つの言語機能に依存して、ターゲットタイプを決定する必要があります:過負荷解析とタイプパラメーター推論。
次の2つの機能インターフェイス(java.lang.runnableおよびjava.util.concurrent.callable <v>)をご覧ください。
public interface runnable {void run(); } public interface callable <v> {v call(); }runnable.run()メソッドは値を返さず、callable.call()メソッドにはそれがあります。
Invokeメソッドに次のように過負荷をかけるとします。
void invoke(runnable r){r.run(); } <t> t Invoke(callable <t> c){return c.call(); }したがって、次のステートメントでどの方法が呼び出されますか。
String s = invoke(() -> "done");
Invoke(callable <t>)は、この方法には戻り値があるため、呼び出されます。この場合、lambda式(() - > "done")のタイプは<t>と呼ばれます。
シリアル化
ラムダ式のターゲットタイプと呼び出すパラメーターのタイプがシリアル化可能である場合、ラムダ式もシリアル化可能です。ただし、内部クラスと同様に、ラムダ式のシリアル化は強く推奨されません。