2013年にリリースされたJavase8には、今年6月のJSR-335ドラフトで説明されているLambda Projectという計画が含まれます。
JSR-335はJavaに閉鎖を導入しました。閉鎖は、C ++、C#などの多くの一般的な言語で存在します。閉鎖により、関数ポインターを作成し、パラメーターとして渡すことができます。この記事では、Java8の特性を大まかに見て、Lambda式を紹介します。そして、いくつかの概念と文法を説明するためにいくつかのサンプルプログラムを掲載しようとします。
Javaプログラミング言語は、インターフェイスの概念を提供し、抽象的なメソッドをインターフェイスで定義できます。インターフェイスはAPIを定義し、ユーザーまたはサプライヤーがこれらのメソッドを実装することを望んでいます。多くの場合、一部のインターフェイスの独立した実装クラスを作成しません。
匿名の使用は広く使用されています。匿名の内部クラスで使用される最も一般的なシーンは、イベントプロセッサです。第二に、匿名の内部クラスは、通常、実行可能/呼び出し可能なインターフェイスの実装クラスを作成する代わりに、匿名の内部クラスを作成することがよくあります。
私たちが議論しているように、匿名のクラスは、忍者の特定のインターフェイスの実装です。通常、この実装クラスのオブジェクトをメソッドのパラメーターとして渡すと、このメソッドは実装クラスメソッドを内部的に実装クラスに呼び出します。したがって、このインターフェイスはコールバックインターフェイスと呼ばれます。
匿名のカテゴリはどこでも使用されていますが、まだ多くの問題があります。最初の主な問題は複雑さです。これらのクラスにより、コードのレベルは乱雑で複雑に見え、垂直的なプリベムとも呼ばれます。第二に、パッケージングクラスの非ファイナルメンバーにアクセスすることはできません。このキーワードは非常に混乱します。匿名のクラスがパッケージクラスと同じメンバー名を持っている場合、この場合は外部メンバーが見えます。このキーワードは、カプセル化オブジェクトではなく、匿名オブジェクト自体に値するためです。
public void anymousexample(){string nonfinalvariable = "nonformal exmple";メソッド変数を実行します。 println( " - >" + variable);}};}。出力は次のとおりです。
- > method varial->実行可能クラスメンバーを実行します
この例では、上記の問題を説明し、ラムダの表現は匿名の内部クラスによって引き起こされるすべての問題をほぼ解決します。ラムダの表現をさらに調査する前に、機能的なインターフェイスを見てみましょう。
機能的なインターフェイス
関数インターフェイスは、このメソッド契約を表す単一のメソッドのみを持つインターフェイスです。
上記の定義の1つだけがそれほど単純ではありません。私はこの段落を理解していません。元のテキストを表示してください。
次の例は、機能的インターフェイスの概念を理解する方法を明確に示しています。
interface runnable {void run();} // functionalinterface foo {boolean equals(object obj);} functional;機能的ではありません。メソッドオブジェクトはpublicInterface x {int m(iterable arg);} Internet y {int m(iterable arg);} internet z x、y {} // functional:2つの方法ですが、同じ署名ほとんどのコールバックインターフェイスは機能的なインターフェイスです。たとえば、Runnable、Callable、Comparatorなど。以前はSAMと呼ばれていました(単一の要約方法)
ラムダの表現
私たちが言ったように、匿名のカテゴリの主な問題は、コードのレベルが乱雑に見えることです。つまり、垂直式の式は匿名ですが、それらの構造はより軽くて短いことです。ラムダの表現は方法のように見えます。それらは、これらのパラメーターの正式なパラメーターリストとブロッキングを持っています。
(string) - > s.lengh;() - > 43;
上記の例は、最初の式がパラメーターとして文字列変数を受信し、文字列の長さを返すことを意味します。パラメーターのない2番目のものと戻り43。最後に、3番目は2つの整数XとYを受け入れ、平和に戻りました。
多くの言葉を読んだ後、私は最初のラムダ式の例を挙げます。
パブリッククラスfirstlambdaexpression {public string variable = "class level variable";文字列non -finalvariable = "これはnon final variable"; new thread(() - > {// compilation error // string variable = "run methos"system.out。println( " - >" " + variable) ; system.out.println( " - >" "" + this.variable);})。出力は次のとおりです。
- >メソッドローカル変数 - >クラスレベル変数
Lambda式と匿名の内部クラスの使用の違いを比較できます。 Lambda式を使用して匿名のカテゴリを書くことで、可視性が可変性の問題が解決することを明確に言うことができます。コードの注釈を見ることができます。
一般的なLambda式の構文には、パラメーターリストが含まれています。矢印キーワード「 - >」は本体です。被験者は、表現(1つのラインステートメント)またはマルチライン文です。それが式である場合、それは計算されて、それがマルチラインのブロックである場合、それはメソッドの系統的なブロックに非常に似ているようです。休憩と続行は、サイクル内でのみ使用できます。
このスタイルは通常C#とScalaにあるため、この特別な文法フォームを選択する理由は、ラムダの表現の一般的な執筆でもあるためです。この文法デザインは、基本的に匿名タイプの複雑さを解決します。しかし、同時に、彼は非常に柔軟です。たとえば、方法が単一の式である場合、ブラケットと返品声明は不要です。表現の結果は、彼自身の返品価値です。この柔軟性は、コードをシンプルに保つことができます。
ラムダ式は匿名として使用されるため、他のモジュールや他のラムダ式(ネストされたラムダ式)で柔軟に使用できます。
//ラムダの博覧会は、メソッドパラメーターブロックで囲まれています。 (() - > {system.out.println( "異なるスレッドでの実行");});
Lambdaの式を詳しく見ると、ターゲットインターフェイスタイプが式の一部ではないことがわかります。コンパイラは、ラムダの発現のタイプと周囲の環境を推測するのに役立ちます。
Lambdaの式にはターゲットタイプが必要であり、可能なターゲットタイプに適応できます。ターゲットタイプがインターフェイスである場合、正しくコンパイルするには、次の条件を満たす必要があります。
コンパイラは、ターゲットタイプステートメントを介してパラメータータイプと数字を知ることができるため、ラムダ式では、パラメータータイプ宣言を省略できます。
Comparator C =(S1、S2) - > S1.comPareToignoreCase(S2);
さらに、ターゲットタイプで宣言されたメソッドが1つのパラメーターのみを受信した場合(多くの場合、そうである場合)、パラメーターの小さなブラケットも記述することができます。
actionlistenr ristenr = event-> event.getWhen();
非常に明白な質問があります、なぜラムダは指定されたメソッド名ではないのですか?
答えは次のとおりです。ラムダ式は機能インターフェイスにのみ使用できますが、機能インターフェイスには1つの方法しかありません。
ラムダ式を作成する機能的インターフェイスを決定すると、コンパイラは機能的なインターフェイス中国の法律の署名を知覚し、与えられた式が一致するかどうかを確認できます。
この柔軟な文法は、匿名の垂直的なプライベートの使用を避けるのに役立ち、水平方向のプリベム(非常に長い1つの文章)をもたらさないでしょう。
ラムダの表現文法はコンテキストに関連していますが、これらは初めてではありません。 Java SE 7によって追加されたダイヤモンドオペレーターには、この概念もあり、これはコンテキストによって推測されます。
void invoke(runnable r){r.run()} void future invoke(callable r){return c.compute()} //上記の2つの方法onal interface -future s = invoke(() - > "done"); /どの呼び出しが呼び出されますか?
上記の質問に対する答えは、呼び出し可能なパラメーターを受信する方法を呼び出すことです。この場合、コンパイラは異なるパラメータータイプの負荷によって解決されます。複数の該当する負荷方法がある場合、コンパイラはLambda発現と対応するターゲットタイプの互換性もチェックします。簡単に言えば、上記のInvokeメソッドは返されると予想されますが、返品値があるのは1つのInvokeメソッドのみです。
Lambda式は、対応するタイプと互換性がある限り、指定されたターゲットタイプに明示的に変換できます。次のプログラムを見ると、3種類のCallableを実装しましたが、それらはすべてクラスタイプに変換しました。
パブリッククラスfirstwithlambdaexpressions {public static void main(string [] args){listl = arrayslist(callial) - > "callable 1"、(callable)() - > "callable 2"、(callable)) - >> 「Callable 3」) ){e1.printstacktrace();} e.shutdown();} public void dumplist(list list)interuptexception、executionexcepti on {for(future:list){system.out.println(future.get()); }}}前に説明したように、匿名のカテゴリは周囲の環境の非臨床変数にアクセスできません。しかし、ラムダの表現にはそのような制限はありません。
現在、定義された関数インターフェイスは、インターフェイスにのみ適用できます。抽象的なメソッドのみのラムダ式を作成しようとしましたが、コンパイルエラーが発生しました。 JSR -335によると、Lambda式の将来のバージョンは機能クラスをサポートする可能性があります。
メソッドの引用
呼び出さずに参照方法として参照される方法。
Lambda式は、匿名の方法を定義し、機能的インターフェイスのインスタンスとして使用することができます。方法は、ラムダの式に非常に似ていますが、違いは、既存のクラスまたはオブジェクトのメソッドを引用していない方法の実装を引用しています。
System :: getProperty "ABC" :: playsString :: qualsuper :: toStringArrayList :: new
上記のステートメントは、メソッドの一般的な構文とコンストラクターへの参照を示しています。ここでは、新しい操作文字を見ました ":::: double colon)参照として、このチュートリアルの範囲内で、単に分離主義者として使用します。
ターゲットリファレンスまたはレシーバーは、プロバイダーとセパレーターの背後に配置されます。これは、メソッドを引用できる式を形成します。最終的な声明では、メソッドの名前は「新しい」です。この式では、アレイリストクラスの構造法を引用しています(次のセクションのコンストラクターへの参照)
これを理解する前に、引用された方法の強さを確認したいと思います。
Import Java.util.list。 = {new Employee( "Nick")、New Employee( "Robin")、New Employee( "Josh")、新しい従業員( "Andy")、新しい従業員( "Mark")}; sort: "); dumpemployee(従業員); arrays.sort(従業員、従業員:: mycompare); system.out.println(" after sort: "); moplyees); Aslist(従業員)){System.out.print(emp.name+"、");} out.println(); (employee emp1、Employee emp2){return emp1.name.compareto(emp2.name);}}}出力は次のとおりです。
ソートの前:ニック、ロビン、ジョシュ、アンディ、マーク、アフターソート:アンディ、ジョシュ、マーク、ニック、ロビン、
出力は特別なものではありません。静的メソッドMyCompareは2つの従業員オブジェクトを受信し、名前を返して比較します。
主な方法では、従業員の別の配列を作成し、それを配列に渡しました。それを式の配列(従業員:: myCompare)に参照しました。
ちょっと待ってください。Javadocを見ると、ソートメソッドの2番目のパラメーターがCoraratorタイプであることがわかりますが、従業員の静的メソッドリファレンスに渡すことがわかります。重要な問題は、従業員に同等のインターフェイスを実装させず、独立したコンパレータクラスを書くこともありませんが、出力に問題はありません。
その理由を見てみましょう。 Arrays.Sortメソッドはコンパレータのインスタンスを期待しており、このコンパレータは機能的なインターフェイスです。つまり、1つの方法しか持っていません。つまり、比較です。ここでは、この式でcompaareメソッドの実装を提供するラムダ式を悪意を持って渡します。しかし、私たちでは、従業員のクラスにはすでに比較方法があります。その名前は異なります。
同じ名前の複数のメソッドがある場合、コンパイラはターゲットタイプに応じて最適なマッチングを選択します。理解するには、例をご覧ください。
public static int mycompare(Employee emp1、Employee emp2){return emp1.name.compareto(emp2.name);} //上記と同じ名前を持つ別のメソッド。 {{){{){{return int1.compareto(int2);}ソート用に2つの異なる配列を作成しました。
従業員= {新しい従業員( "Nick")、New Employee( "Robin")、新しい従業員( "Josh")、新しい従業員( "Andy")、New Employee( "Mark"); ins = {1、4、8、2、3、8、6};次に、次の2行のコードを実行します
arrays.sort(従業員、mycompare);
ここでは、コードの2行の参照方法は同じです(従業員:: MyCompare)。
静的メソッドに惑わされないでください。また、サンプルメソッドへの参照を作成することもできます。静的メソッドの場合、クラス名::メソッドの参照を書き込む場合、それはオブジェクト::メソッド名です。
上記の例は非常に優れていますが、整数が同等の実装を実装し、実装方法の比較を提供しているため、整数の比較の方法を記述する必要はありません。したがって、次の行を直接使用するだけです。
arrays.sort(ints、integer :: compareto);
これを見て、少し混乱していると感じますか?いいえ?次に、整数はクラス名ではありません(new integer()のようなインスタンスではありませんが、整数クラスのメンバーメソッドですメンバーメソッドが参照されます::それは以前にオブジェクトである必要がありますが、ここでの文が実際に合法である理由。
答えは次のとおりです。このタイプのステートメントにより、特定のタイプで使用できます。整数はデータ型であり、データ型の場合、このステートメントは許可されています。
従業員メソッドをmycompareを非段階的に変換してから使用すると、従業員:: mycompareを使用すると、コンピレーションエラーがあります。適切な方法は見つかりません。
建設的な方法参照
コンストラクター参照は、指定された制度化なしでコンストラクターを参照するクラスとして使用されます。
建設的な方法参照は、Javase 8の新機能です。建設的な方法への参照を構築し、ターゲットタイプにパラメーターとして渡すことができます。
参照に使用する場合、それらを使用するために1つの既存の方法を引用します。同様に、建設的な方法参照を使用する場合、既存の建設的な方法への参照を作成します。
前のセクションでは、Constructor :: Newが参照する文法名を見てきました。これはメソッドリファレンスのように見えます。この構築されたメソッドへの参照は、ターゲット機能インターフェイスのインスタンスに割り当てることができます。この場合、クラスに複数のコンストラクターが存在する場合があります。
私にとって、最初の建設的な方法を書くことは困難です。最後に、私は一生懸命働き、最後に「ああ、私は見つけました...」、次の手順を見てください。
public constructionorReferences {public static void main(string [] ar){myclass :: new.out.println();}} interface {myclass getmemyobject()} class {myclass(){} {} }出力は次のとおりです。
- > com.myclass@34e5307e
これは少し驚くべきことです。このインターフェイスと、インターフェイスで宣言された方法がMyClassタイプであることを除いて、このクラスの返品値はありません。
この例は、私の心の中で別の問題を引き起こしました。パラメーターを使用して建設的な方法をインスタンス化する方法は?以下の手順をご覧ください。
Public ConstructorReferences {public static void main(string [] ar){emlpoyeproder = employee :: new; System.out.println( " - >従業員年齢:"+emp.age)} emlpoyeeprovider {String S、Integer I);}年齢){this.name = name; age = age;}}出力は次のとおりです。
- >従業員名:ジョン - >従業員年齢:30
この記事を読む前に、javase8-defaultメソッドの最もクールな機能を見てみましょう
デフォルトの方法
Javase8は、デフォルトメソッドと呼ばれる概念を導入します。インターフェイスの初期のJavaバージョンには、インターフェイスには、すべての抽象的なメソッドのステートメントが含まれています。今後のJavaバージョンでは、インターフェイス内のメソッドのデフォルトの実装が許可されています。それほどナンセンスではありません、次のことを見てください:
public class defaultmethods {public static void main(string [] ar){norme interfaceimpl(); System.out.println( " - > myDefaultMethod");}} class normalinterfacempl emplemelemerInterface {@Override public void mynormalmethod()system.out.println( " - > mynormalmethod");}}}}出力は次のとおりです。
- > myDefaultMethod
上記のインターフェイスは2つのメソッドを宣言しますが、MyDefaultMethodはデフォルトの修飾子を使用し、デフォルトの実装のメソッドブロックを提供するため、このインターフェイスの実装クラスはそれらの1つのみを実現します。 GMの重い負荷ルールはまだここで有効です。実装クラスがインターフェイス内のメソッドを実装する場合、呼び出し時にコールクラスのメソッドになります。そうしないと、デフォルトの実装が呼び出されます。
統合された親インターフェイスのインターフェイスは、親インターフェイスのデフォルトの実装を増加、変更、および削除することができます。
Interface ParentInterface {void intialnomal(); - > intialnomal ");} void void veritialsdefault(); //通常の方法}この例では、1つは正常であり、デフォルトでは最初のメソッドが実装されています。
クラスがクラスCを継承し、インターフェイスIを実現し、Cにはメソッドがあり、デフォルトのメソッドが互換性があるデフォルトメソッドを提供するメソッドを想像してください。この場合、CのメソッドはIのデフォルトメソッドを優先し、Cのメソッドでさえ、メソッドが抽象的である場合でも優先されます。
public class defaultmethods {public static void main(string [] ar){interfaxe emply interfaceimpl(); ;}} interface interfaxe {public void defaultmethod()default {system.out.println( " - > interfaxe");出力は次のとおりです。
- > parentclass
2番目の例は、私のクラスが2つの異なるインターフェイスを実装していることですが、2つのインターフェイスの両方が同じデフォルトの実装方法で同じステートメントを提供します。この場合、コンパイラは何が起こっているのかを把握できません。これは、次の方法で実行できます。
public class defaultmethods {public static void main(string [] ar){new normalinterfampl(); );}} interface second interface {public void defaultmethod()default {system.out.println( " - > secondinterface");出力は次のとおりです。
- > SecondInterface
今、私たちはJava閉鎖の導入を読みました。この記事では、JavaのLambda式、メソッド参照、およびコンストラクターの参照を理解する機能的インターフェイスとJava閉鎖と接触しました。また、ラムダの表現のHello Worldの例も書きました。
Javase8はすぐに来ています。