Javaで可能な限りラムダ式を使用していますか?


52

私は最近、Java 8で導入されたLambda式を習得しました。関数型インターフェースを使用するときはいつでも、関数型インターフェースを実装するクラスを作成する代わりに、常にLambda式を使用する傾向があります。

これは良い習慣と考えられていますか?または、機能インターフェイスにLambdaを使用することが適切でない状況ですか?


122
質問には「可能性は良い練習いつでも技術のXを使用している」 だけ正しい答えは「ノー、利用技術のXません可能な限り、しかしいつでも賢明な」
Doc Brown

ラムダ(匿名)と他の種類の機能実装(メソッド参照など)を使用するタイミングの選択は、本当に興味深い質問です。数日前にJosh Blochに会う機会がありましたが、これは彼が話していたポイントの1つでした。彼が言ったことから、私は彼がこの正確な質問に対処するEffective Javaの次のエディションに新しいアイテムを追加することを計画していることを理解しています。
ダニエル・プライデン

@DocBrownそれは答えであって、コメントではないはずです。
gnasher729

1
@ gnasher729:絶対にありません。
Doc Brown

回答:


116

ラムダを使用しないことを考慮する必要がある多くの基準があります。

  • サイズラムダが大きくなると、ラムダを取り巻くロジックに従うのが難しくなります。
  • 繰り返しロジックを繰り返すために名前付き関数を作成する方が良いですが、非常に単純なラムダをばらばらに繰り返すことは問題ありません。
  • 命名すばらしいセマンティック名を考えることができる場合、代わりにそれを使用する必要があります。コードに多くの明快さを追加するからです。私のような名前を話していませんpriceIsOver100x -> x.price > 100その名前と同じくらい明確です。isEligibleVoterそのような名前は、条件の長いリストを置き換えることを意味します。
  • ネストネストされたのラムダは、実際にある、本当に読みにくいです。

船外に出ないでください。ソフトウェアは簡単に変更できます。疑問がある場合は、両方の方法で書き、どちらが読みやすいかを確認してください。


1
ラムダは少量のデータ処理では効率が悪いため、ラムダを介して処理されるデータの量を考慮することも重要です。それらは、大規模なデータセットの並列化に非常に役立ちます。
CraigR8806

1
@ CraigR8806ここでパフォーマンスが問題になるとは思わない。匿名関数を使用するか、機能的インターフェースを拡張するクラスを作成することは、同じパフォーマンスであるはずです。パフォーマンスの考慮事項は、命令型スタイルループでstreams / higher-order-functions apiを使用することについて話すときに出てきますが、この質問では、どちらの場合でも異なる構文で関数型インターフェイスを使用しています。
プーレン

17
「非常に単純なラムダをばらばらに繰り返すことは問題ありませんが、それらが一緒に変化する必要がない場合のみ。彼らは場合にしなければならないすべてのコードが正しいとするため、同じロジックであることの変更は一つだけの場所で行われる必要があるので、あなたはそれらをすべて実際には同じ方法で行う必要があります。
jpmc26

全体的にこれは本当に良い答えです。ラムダの代替としてメソッド参照の使用に言及すると、この答えにある穴だけにパッチが適用されます。
モルゲン

3
疑問がある場合は、両方の方法で書き、読みやすいコードを管理している他の人に尋ねてください。
corsiKa

14

場合によります。異なる場所で同じラムダを使用していることに気づいたときはいつでも、インターフェースを実装するクラスの実装を検討する必要があります。しかし、匿名の内部クラスを使用した場合は、ラムダの方がはるかに優れていると思います。


6
メソッド参照を使用する可能性を忘れないでください!オラクルは、まさにその理由から、静的メソッドとデフォルトメソッドをいたるところに追加しています(そして、Java 9でさらに追加しています)。
ヨルグWミットタグ

13

カールビーレフェルトの答えを支持しますが、簡単に追加したいと思います。

  • デバッグ 一部のIDEは、ラムダ内のスコープとの闘い、およびラムダのコンテキスト内のメンバー変数の表示に苦労しています。願わくば、この状況が今後変化することを期待していますが、ラムダが散らばっているときに他の人のコードを維持するのは面倒です。

.NETには間違いなく当てはまります。必ずしもラムダを避けさせませんが、代わりに何か他のことをしたとしても、私は確かに罪悪感を感じません。
user1172763

3
その場合は、間違ったIDEを使用しています。IntelliJとNetbeansはどちらも、その特定の分野で非常にうまく機能しています。
デビッドフォースター

1
@ user1172763 Visual Studioでは、メンバー変数を表示する場合、ラムダのコンテキストに到達するまで呼び出しスタックを上に移動できることがわかりました。
ゼフスピッツ

6

囲みスコープのローカル変数へのアクセス

Karl Bielefeldtが受け入れた回答は正しいです。もう1つ区別を追加できます。

  • 範囲

クラス内のメソッド内にネストされたラムダコードは、そのメソッドとクラス内にある効果的に最終変数にアクセスできます。

機能的なインターフェースを実装するクラスを作成しても、呼び出しコードの状態に直接アクセスすることはできません。

Javaチュートリアル(エンファシスマイニング)を引用するには

ローカルクラスおよび匿名クラスと同様に、ラムダ式は変数をキャプチャできます。それらは、囲むスコープのローカル変数への同じアクセス権を持ちます。ただし、ローカルクラスや匿名クラスとは異なり、ラムダ式にはシャドウイングの問題はありません(詳細については、シャドウイングを参照してください)。ラムダ式のスコープはレキシカルです。つまり、スーパータイプから名前を継承せず、新しいレベルのスコープを導入しません。ラムダ式の宣言は、囲んでいる環境の場合と同じように解釈されます。

そのため、長いコードを引き出して命名することには利点がありますが、それを囲むメソッドとクラスの状態への直接アクセスの単純さに対して、それを比較検討する必要があります。

見る:


4

これは簡単な選択かもしれませんが、他の回答で作成された他のすべての優れた点に、私は追加します:

可能な場合は、メソッド参照を優先します。比較:

employees.stream()
         .map(Employee::getName)
         .forEach(System.out::println);

employees.stream()
         .map(employee -> employee.getName())
         .forEach(employeeName -> System.out.println(employeeName));

メソッド参照を使用すると、あなたのような怠惰な名前に通常の冗長および/またはリードであるラムダの引数(複数可)、名前を付ける必要が保存されますeかをx


0

Matt McHenryの答えに基づいて、ラムダとメソッド参照の間には、ラムダを一連のメソッド参照として書き直すことができる関係があります。例えば:

employees.stream()
         .forEach(employeeName -> System.out.println(employee.getName()));

employees.stream()
         .map(Employee::getName)
         .forEach(System.out::println);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.