Java関数式プログラミング(七)閉パッケージ


構文の役割ドメインと閉パッケージの使用
多くの開発者は、lambda式を使用するとコードが冗長になり、コードの品質が低下するという誤解を持っています.逆に,コードがどんなに複雑になっても,コードの簡潔性のためにコード品質に妥協することはなく,以下に示す.
前の例ではlambda式を再利用することができます.しかし、別のアルファベットに一致すれば、コード冗長性の問題はすぐに巻き返されます.まず、この問題をさらに分析し、文法の役割ドメインと閉パッケージで解決します.
Lambda式による冗長性
friendsからNまたはBで始まるアルファベットをフィルタします.上記の例を延用し続けると、私たちが書いたコードは次のようになります.

final Predicate<String> startsWithN = name -> name.startsWith("N");
final Predicate<String> startsWithB = name -> name.startsWith("B");
final long countFriendsStartN =
friends.stream()
.filter(startsWithN).count();
final long countFriendsStartB =
friends.stream()
.filter(startsWithB).count();

1番目のpredicateは名前がNで始まるかどうかを判断し、2番目はBで始まるかどうかを判断します.この2つのインスタンスをそれぞれ2回のfilterメソッド呼び出しに渡します.これは合理的に見えますが、2つのpredicateは冗長性を生み出し、それらはその検査のアルファベットが異なるだけです.このような冗長性を回避する方法を見てみましょう.
ワード・ドメインを使用して冗長性を回避
最初のスキームでは,アルファベットを関数のパラメータとして抽出し,同時にこの関数をfilter法に渡すことができる.これはいい方法ですが、filterはどんな関数でも受け入れるわけではありません.パラメータが1つしかない関数だけを受け入れます.そのパラメータは集合内の要素に対応し、boolean値を返します.Predicateを入力したいと思っています.
パラメータが渡されるまでこのアルファベットを先にキャッシュしたい場所があります(ここではnameというパラメータです).次はこのような関数を新規作成します.

public static Predicate<String> checkIfStartsWith(final String letter) { 
return name -> name.startsWith(letter);
}

Stringパラメータを受信し、後で使用するためにfilterメソッドに渡すことができるPredicateオブジェクトを返す静的関数checkIfStartsWithを定義した.前に見た高次関数のように関数をパラメータとしているのではなく,この方法は関数を返す.しかし、それは12ページの高次関数でもあります.
変革ではなく進化に言及した.
checkIfStartsWithメソッドで返されるPredicateオブジェクトは、他のlambda式とは少し異なります.return name->name.startsWith(letter)文では、lambda式に伝達されるパラメータであるnameが何であるかがよくわかります.しかし、変数letterはいったい何ですか?この匿名関数のドメインの外にあり、Javaはこのlambda式を定義するドメインを見つけ、この変数letterを発見した.これを文法の役割ドメインと言います.構文役割ドメインは、後で別のコンテキストで使用できるように、使用ドメインに変数をキャッシュできる便利なものです.このlambda式はその定義ドメイン内の変数を用いるため,この場合も閉パケットと呼ぶ.文法の役割ドメインへのアクセス制限については、31ページの
文法の役割ドメインには何か制限がありますか?

文法の役割ドメインには何か制限がありますか?


Lambda式では、定義されたドメイン内のfinalタイプまたは実際にはfinalタイプのローカル変数にのみアクセスできます.


Lambda式はすぐに呼び出されるか、呼び出されるのを遅らせるか、異なるスレッドから呼び出される可能性があります.競合競合を回避するために、定義されたドメイン内のローカル変数にアクセスします.初期化後は変更は許可されません.変更操作によってコンパイル異常が発生します.


finalとマークしてこの問題を解決しましたが、Javaは私たちに必ずマークするように強制していません.実際、Javaは2時を見ています.1つは、この変数にアクセスするには、定義方法で初期化を完了し、lambda式を定義する前にする必要があります.第二に、これらの変数の値は変更できません.つまり、実際にはfinalタイプですが、このようなタグはありません.


ステータスのないlambda式は実行時定数であり、ローカル変数を使用したlambda表現には追加の計算オーバーヘッドがあります.

filterメソッドを呼び出すと、checkIfStartsWithメソッドで返されるlambda式を使用できます.

final long countFriendsStartN =
friends.stream() .filter(checkIfStartsWith("N")).count();
final long countFriendsStartB = friends.stream()
.filter(checkIfStartsWith("B")).count(); 

filterメソッドを呼び出す前にcheckIfStartsWith()メソッドを呼び出し、希望のアルファベットを入力します.この呼び出しはすぐにlambda式を返し、filterメソッドに渡します.
高次関数(ここではcheckIfStartsWith)を作成し、構文ドメインを使用することで、コードの冗長性を除去することに成功しました.nameがアルファベットで始まるかどうかを繰り返し判断する必要はありません.
リファクタリング
前の例ではstaticメソッドを使用しましたが、staticメソッドで変数をキャッシュしたくないので、コードを混乱させました.この関数の役割ドメインを使用する場所に縮小することが望ましい.これをFunctionインタフェースで実現できます.

final Function<String, Predicate<String>> startsWithLetter = (String letter) -> {
Predicate<String> checkStarts = (String name) -> name.startsWith(letter);
return checkStarts; };
 
このlambda式は元のstatic法に取って代わって、関数の中に入れることができて、それを使う前に定義すればいいです.startWithLetter変数は、入力パラメータがStringであり、出力パラメータがPredicateのFunctionであることを参照します.
staticメソッドを使用するよりも、このバージョンは簡単ですが、より簡潔に再構築し続けることもできます.実際の観点から見ると、この関数は前のstatic方法と同じです.いずれもStringを受信してPredicateを返します.Predicateを明示的に宣言しないために、lamdba式を全体に置き換えます.

final Function<String, Predicate<String>> startsWithLetter = (String letter) -> (String name) -> name.startsWith(letter);
  
私たちはそれらのめちゃくちゃなものを乾かしましたが、タイプ宣言を削除して、Javaコンパイラがコンテキストに基づいてタイプを導くことができます.改善されたバージョンを見てみましょう.

final Function<String, Predicate<String>> startsWithLetter =
letter -> name -> name.startsWith(letter); 
   
このような簡潔な文法に適応するには少し工夫しなければならない.もしそれがあなたの目が見えなくなったら、まず他の場所を見てみましょう.コードの再構築が完了し、元のcheckIfStartsWith()メソッドを置き換えることができます.

final long countFriendsStartN = friends.stream()
.filter(startsWithLetter.apply("N")).count();
final long countFriendsStartB = friends.stream()
.filter(startsWithLetter.apply("B")).count();
   
このセクションでは,高次関数を用いた.関数を別の関数に渡す場合、関数に関数を作成する方法、関数を介して関数を返す方法を見ました.これらの例はlambda式がもたらす簡潔性と再利用性を示している.
このセクションではFunctionとPredicateの役割を十分に果たしていますが、それらの違いを見てみましょう.Predicateは、Tタイプのパラメータを受け入れ、対応する判断条件の真偽を表すboolean値を返します.条件判断が必要な場合は、Predicategを使用して完了することができます.フィルタのような要素をフィルタする方法では、パラメータとしてPredicateが受信されます.一方Funcitonは,T型の変数のパラメータを表し,R型の結果を返す関数である.booleanのみを返すPredicateよりも一般的です.入力を1つの出力に変換すればFunctionを使用できるので、mapがFunctionをパラメータとして使用するのも当然です.
コレクションから要素を選択するのは簡単です.次に、コレクションから1つの要素だけを選択する方法について説明します.
未完は続きますので、後続の文章に引き続きご注目ください
Java翻訳ステーション .
オリジナル記事の転載は出典を明記してください.
http://it.deepinmind.com