JavaでのLambdaの表現に関する私の見解は、かなり絡み合っています:
このように考えてみてください。LambdaExpressionsは、Javaプログラムの読書体験を減らします。 Javaプログラムは、表現力において傑出したものではありませんでした。それどころか、Javaを人気にしている要因の1つは、そのセキュリティと保守主義です。初心者でさえ、注意を払う限り、堅牢で手入れの行き届いやすいコードを書くことができます。 Lambdaの表現は、開発者にとって比較的高い要件を持っているため、メンテナンスの困難さも増しています。
もう1つのことは、コードコードとして、言語の新機能を学び、受け入れる必要があることです。読書の経験が悪いという理由だけで表現力豊かな強みをあきらめた場合、一部の人々は三眼式の表現でさえ理解するのが難しいと感じます。言語も発展しており、追いつくことができない人は自発的に残されます。
私は取り残されたくありません。しかし、私が選択をしなければならなかった場合、私の決定はまだ比較的保守的でした:Java言語でLambdaを使用する必要はありません - それは現在のJava Circleの多くの人々をそれに慣れさせず、人件費の増加を引き起こします。あなたがそれをとても気に入っているなら、あなたはScalaの使用を検討することができます。
とにかく、私はまだラムダを習得しようとし始めました。結局のところ、仕事で維持されているコードの一部はラムダを使用しています(私を信じてください、徐々に削除します)。学習すべきチュートリアルは、公式のOracle Java Webサイトの関連チュートリアルです。
- - - - - - -
現在、ソーシャルネットワークアプリケーションが作成されていると仮定します。 1つの機能は、管理者がメッセージの送信など、指定された基準を満たすメンバーに対して特定のアクションを実行できることです。次の表には、このユースケースが詳細に説明されています。
| 分野 | 説明する |
| 名前 | 実行するアクション |
| 主要な参加者 | 管理者 |
| 前提条件 | 管理者はシステムにログインします |
| ポストコンディション | 指定された基準を満たすメンバーに対してのみアクションを実行する |
| 主な成功シナリオ | 1.管理者は、ターゲットメンバーが操作を実行するためのフィルタリング基準を設定します。 2。管理者は、実行するアクションを選択します。 3.管理者は送信ボタンをクリックします。 4.システムは、指定された基準を満たすメンバーを見つけます。 5.システムは、指定された基準を満たすメンバーに対して事前に選択された操作を実行します。 |
| 拡張 | 実行操作を選択する前、または[送信]ボタンをクリックする前に、管理者はフィルタリング基準を満たすメンバー情報をプレビューするかどうかを選択できます。 |
| 発生頻度 | それは一日に何度も起こります。 |
次の個人クラスを使用して、ソーシャルネットワークのメンバー情報を表します。
Public Class Person {public Enum sex {男性、女性}文字列名;ローカルデートの誕生日;性別;文字列emailAddress; public int getage(){// ...} public void printperson(){// ...}}すべてのメンバーがリスト<パーソン>インスタンスに保存されていると仮定します。
このセクションでは、非常にシンプルな方法から始めて、ローカルクラスと匿名クラスを使用して実装しようとします。最後に、ラムダ表現のパワーと効率を徐々に体験します。完全なコードはここにあります。
ソリューション1:指定された基準を1つずつ満たすメンバーを見つける方法を作成する
これは、前述のケースを実装するための最も簡単で粗いソリューションです。いくつかの方法を作成することであり、各方法は基準(年齢や性別など)を検証します。次のコードは、年齢が指定された値よりも古いことを確認します。
public static void printpersonsolderthan(list <person> outroster、int age){for(person p:outroster){if(p.getage()> = age){p.printperson(); }}}これは非常に脆弱なソリューションであり、少しの更新のためにアプリケーションが実行されない可能性が非常に高いです。人クラスに新しいメンバー変数を追加するか、標準の年齢を測定するためにアルゴリズムを変更する場合、この変更に適応するために多くのコードを書き直す必要があります。さらに、ここでの制限は硬すぎます。たとえば、指定された値よりも若いメンバーを印刷したい場合はどうすればよいですか?別の新しい方法を追加するprintpersonsyoungerthan?これは明らかに愚かな方法です。
ソリューション2:より一般的な方法を作成します
次の方法は、printpersonsolderthanよりも適応性があります。この方法は、指定された年齢層内でメンバー情報を印刷します。
public static void printpersonswithinagerange(list <person> loster、int low、int high){for(person p:oster){if(low <= p.getage()&& p.getage()<high){p.printperson(); }}}今、新しいアイデアがあります。指定された性別のメンバー情報を印刷したい場合、または指定された性別を満たし、指定された年齢層内にある場合はどうすればよいですか?人クラスを調整し、友情や地理的な場所などのプロパティを追加した場合はどうなりますか。このようなメソッドを書くことはprintPersonsyoungerthanよりも普遍的ですが、可能なすべてのクエリの方法を書くことは、コードの脆弱性にもつながる可能性があります。標準のチェックコードを新しいクラスに入れることをお勧めします。
ソリューション3:ローカルクラスに標準検査を実装します
次の方法では、検索基準を満たすメンバー情報を印刷します。
public static void printpersons(list <person>名簿、チェックパーソンテスター){for(person p:outroster){if(tester.test(p)){p.printperson(); }}}CheckPerso Object Testerがプログラムで使用され、リストパラメーター名簿の各インスタンスを検証します。 tester.test()がtrueを返す場合、printperson()メソッドが実行されます。検索基準を設定するには、CheckPersonインターフェイスを実装する必要があります。
次のクラスはチェックピンを実装し、テスト方法の特定の実装を提供します。このクラスのテスト方法は、米国の兵役の要件を満たすメンバーシップに関する情報、つまり男性の性別と18歳から25歳の年齢です。
クラスCheckPersonEligibleForsectiveServiceはCheckPerson {public boolean Test(person p){return p.gender == person.sex.male && p.getage()> = 18 && p.getage()<= 25; }}このクラスを使用するには、インスタンスを作成してPrintPersonsメソッドをトリガーする必要があります。
PrintPersons(名簿、新しいCheckPersonElifibleForselectiveService());
コードの脆弱性は低く見えます - 人クラスの構造が変更されているため、コードを書き換える必要はありません。ただし、ここにはまだ追加のコードがあります。アプリケーションの各検索標準の内部クラスを定義する新たに定義されたインターフェイス。
CheckPersonEligibleForSectiveSiveServiceはインターフェイスを実装するため、匿名のクラスを各標準の内部クラスを定義せずに使用できます。
ソリューション4:匿名クラスを使用して、標準検査を実装します
以下のprintpersonsメソッドの1つのパラメーターは、匿名クラスです。この匿名のクラスの機能は、スキーム3のCheckPersonSelectiveSectiveserviceクラスの機能と同じです。これらはすべて、18歳から25歳までの男性の性別と年齢を持つフィルターメンバーです。
printPersons(名簿、新しいcheckperson(){public boolean test(person p){return p.getgender()== person.sex.male && p.getage()> = 18 && p.getage()<= 25;}});このスキームは、実行される各検索スキームの新しいクラスを作成する必要がなくなったため、エンコードの量を減らします。ただし、これを行うのはまだ少し不快です。チェックパーソンインターフェイスには1つの方法しかないが、実装された匿名クラスはまだ少し冗長でかさばっている。この時点で、Lambda式を使用して匿名クラスを置き換えることができます。以下では、匿名クラスを置き換えるためにラムダの式を使用する方法を説明します。
ソリューション5:Lambda式を使用して、標準チェックを実装します
チェックパーソンインターフェイスは機能的なインターフェイスです。いわゆる機能インターフェイスは、1つの抽象的なメソッドのみを含む任意のインターフェイスを指します。 (関数インターフェイスには、複数のデフォルトメソッドまたは静的メソッドを持つこともできます)。関数インターフェイスには抽象メソッドが1つしかないため、この機能インターフェイスのメソッドを実装するときにメソッドのメソッド名を省略できます。このアイデアを実装するには、匿名のクラス式をラムダ式に置き換えることができます。以下に書き換えたPrintPersonsメソッドでは、関連するコードが強調表示されます。
PrintPersons(名簿、(Person P) - > p.getGender()== person.sex.male && p.getage()> = 18 && p.getage()<= 25);
ここでは、標準の関数インターフェイスを使用してチェックパーソンインターフェイスを置き換えることもできます。これにより、コードがさらに簡素化されます。
ソリューション6:Lambda式で標準機能インターフェイスを使用します
チェックパーソンインターフェイスを見てみましょう。
インターフェイスCheckPerson {Boolean Test(Person P); }これは非常にシンプルなインターフェイスです。抽象的なメソッドは1つしかないため、機能的なインターフェイスでもあります。この抽象的なメソッドは、1つのパラメーターのみを受け入れ、ブール値を返します。この抽象的なインターフェイスは非常に簡単なので、アプリケーションでこのようなインターフェイスを定義する必要があるかどうかを検討します。現時点では、JDKで定義された標準の機能インターフェイスの使用を検討し、java.util.functionパッケージの下にこれらのインターフェイスを見つけることができます。
この例では、Predicate <t>インターフェイスを使用して、チェックパーソンを置き換えることができます。このインターフェイスには、ブールテスト(T T)メソッドがあります。
インターフェイスPredicate <t> {boolean test(t t); }Predicate <t>インターフェイスは、汎用インターフェイスです。一般的なクラス(または汎用インターフェイス)は、一対の角度ブラケット(<>)を使用して1つ以上のタイプパラメーターを指定します。このインターフェイスには、タイプパラメーターは1つだけです。具体的なクラスを使用して一般的なクラスを宣言またはインスタンス化すると、パラメーター化されたクラスが取得されます。たとえば、パラメーター化されたクラスの述語<パーソン>は次のようなものです。
インターフェイスPredicate <serson> {boolean test(person t); }このパラメーター化されたクラスには、checkperson.boolean test(person p)メソッドのパラメーターと戻り値と一致するメソッドがあります。したがって、次の方法で示されているように、Predicate <t>インターフェイスを使用して、チェックパーソンインターフェイスを置き換えることができます。
public static void printpersonswithspredicate(list <serpures>名簿、predicate <serson> tester){for(person p:oster){if(tester.test(p)){p.printperson(); }}}次に、次のコードを使用して、プラン3のように兵役のメンバーをフィルタリングします。
printPersonswithSpredicate(名簿、p-> p.getGender()== person.sex.male && p.getage()> = 18 && p.getage()<= 25);
Predicate <serson>をパラメータータイプとして使用する場合、明示的なパラメータータイプが指定されていないことに気付きましたか。これは、ラムダ式が適用される唯一の場所ではありません。次のスキームでは、Lambda式のより多くの使用法を導入します。
ソリューション7:アプリケーション全体でlambda式を使用します
PrintPersonsSwithPredicateメソッドを見て、ここでLambda式を使用できるかどうかを検討しましょう。
public static void printpersonswithspredicate(list <serpures>名簿、predicate <serson> tester){for(person p:oster){if(tester.test(p)){p.printperson(); }}}この方法では、名簿の各インスタンスがPredicate Instanceテスターを使用してチェックされます。人インスタンスがテスターで定義されているチェック基準に準拠している場合、PrintPerson Method of the Personインスタンスがトリガーされます。
PrintPersonメソッドのトリガーに加えて、テスター標準を満たす人インスタンスも他の方法を実行することができます。ラムダ式を使用して実行する方法を指定することを検討できます(この機能は良いと思います。これは、Javaのメソッドをオブジェクトとして渡すことができない問題を解決します)。これで、printpersonメソッドと同様のラムダ式が必要です。これは、1つのパラメーターのみを必要とし、無効を返すラムダ式です。 1つのことを覚えておいてください。LambdaExpressionsを使用するには、最初に機能インターフェイスを実装する必要があります。この例では、機能的なインターフェイスが必要で、抽象的なメソッドは1つだけ含まれています。この抽象的なメソッドには、タイプの人のパラメーターがあり、voidに戻ります。 JDKが提供する標準の機能インターフェイスConsumer <t>を見ることができます。次のコードでは、消費者<t>のインスタンスを使用して、p.printperson()の代わりに受け入れメソッドを呼び出します。
public static void processpersons(list <person>名簿、predicate <serson> tester、consumer <serson> block){for(person p:outroster){if(tester.test(p)){block.accept(p); }}}それに対応して、次のコードを使用して、兵役時代のメンバーをフィルタリングできます。
ProcessPersons(名簿、p-> p.getGender()== person.sex.male && p.getage()> = 18 && p.getage()<= 25、p-> p.printperson());
メンバー情報を印刷するだけでなく、メンバーシップの検証、メンバーの連絡先情報の取得など、より多くのことをしたい場合は、この時点で、返品値方法を備えた機能インターフェイスが必要です。 JDKの標準機能インターフェイス関数<t、r>には、このr適用(t t)のような方法があります。次の方法では、パラメーターマッパーからデータを取得し、これらのデータのパラメーターブロックで指定された動作を実行します。
public static void ProcessOnsswith機能(リスト<パーソン>名簿、Predicate <serson> tester、function <person> mapper、consumer <string> block){for(person p:roster){if(tester.test(p)){string data = mapper.apply(p); block.accept(data); }}}次のコードでは、名簿の兵役時代のすべてのメンバーの電子メール情報を取得し、それを印刷します。
ProcessPersSonswithFunction(名簿、p-> p.getGender()== person.sex.male && p.getage()> = 18 && p.getage()<= 25、p-> p.getemailaddress()、email-> system.out.println(email));
ソリューション8:ジェネリックをより頻繁に使用します
プロセスパソコンの機能機能を確認しましょう。以下は、この方法の一般的なバージョンです。新しい方法では、パラメータータイプでより多くの耐性が必要です。
public static <x、y> void processelements(iterable <x> source、predicate <x> tester、function <x、y> mapper、consumer <y> block){for(x p:source){if(tester.test(p)){y data = mapper.apply(p); block.accept(data); }}}適切な年齢で兵役のためのメンバー情報を印刷するには、次のようなプロセスエレメント方法を呼び出すことができます。
processelements(名簿、p-> p.getgender()== person.sex.male && p.getage()> = 18 && p.getage()<= 25、p-> p.getemailaddress()、email-> system.out.println(電子メール));
メソッドコールプロセス中に、次の動作が実行されます。
コレクションからオブジェクト情報を取得するこの例では、コレクションインスタンス名簿から個人のオブジェクト情報を取得します。
Predicate Instance Testerに一致するオブジェクトをフィルターします。この例では、述語オブジェクトは、適切な年齢で兵役をフィルタリングする条件を指定するラムダ式です。
ろ過されたオブジェクトは、処理のために関数オブジェクトマッパーに引き渡され、マッパーはこのオブジェクトの値と一致します。この例では、関数オブジェクトマッパーは、各メンバーのメールアドレスを返すLambda式です。
マッパーが一致する値に対して、消費者オブジェクトブロックによる動作を指定します。この例では、消費者オブジェクトはラムダ式であり、これは文字列を印刷する機能であり、関数インスタンスマッパーによって返されるメンバーのメールアドレスです。
ソリューション9:ラムダ式をパラメーターとして使用して集約操作を使用します
次のコードでは、集約操作を使用して、名簿コレクションの軍人メンバーのメールアドレスを印刷します。
Roster.stream().filter(p-> p.getGender()== person.sex.male && p.getage()> = 18 && p.getage()<= 25).map(p-> p.getemailaddress()).foreach(email-> system.out.println(email));
上記のコードの実行プロセスを分析し、次の表を整理します。
行動 | 集約操作 |
オブジェクトを取得します | ストリーム<e> stream() |
述語インスタンスの指定された基準に一致するオブジェクトをフィルター | ストリーム<t>フィルター(述語<?super t>予測) |
関数インスタンスを介してオブジェクトの一致する値を取得します | <r>ストリーム<r>マップ(function <?super t、? |
消費者インスタンスで指定された動作を実行します | void foreach(消費者<?スーパーt>アクション) |
テーブル内のフィルター、マップ、およびforeach操作はすべて集計操作です。集約操作によって処理される要素は、コレクションから直接ではなく、ストリームから得られます(つまり、この例プログラムで呼ばれる最初の方法はStream())。ストリームはデータシーケンスです。コレクションとは異なり、Streamは特定の構造でデータを保存しません。代わりに、Streamは、コレクションなどの特定のソースからパイプラインを介してデータを取得します。パイプラインは、この例では、フィルターマップフォーチャーの例では、ストリーム操作シーケンスです。さらに、集約操作は通常、ラムダ式をパラメーターとして使用します。これは、多くのカスタムスペースも提供します。