
【Java】ラムダ式のプログラミングを学ぼう
皆さんはラムダ式と呼ばれる文法についてご存じでしょうか。私は業務で少し使用した経験があります。
たまにラムダ式を使用している開発業務を見かけることがありますが、そもそもラムダ式とはなにか、メリット・デメリットはなにかを記事作成を通して調査しまとめてみました。
1. ラムダ式とは

ラムダ式と呼ばれるプログラミング言語は、無名関数(英語: anonymous functionあるいはnameless function)に属しており、名前を付けられずに定義された関数のことを指します。
由来としては、「計算式を書く時に同じ式を何度も書くと大変なので、対象の関数をΛ(ラムダ)と置いて記述を省略する」というところから来ているそうです。
ラムダ式の歴史としては2014年にリリースされたJava 8から使用することができるようになりました。
Javaだけではなく、C++やC#、Javascript・PHP・Pythonなど多くのプログラミング言語で対応しています。
特徴としては、メソッドを変数のように扱える点にあります。
また、「匿名クラス(クラス名がなく、その場で使い捨てるクラス)」の実装を簡潔に実装できます。
2. ラムダ式のメリット

1.コード量が減る
関数の名前を考える必要はなく、余計な手間がかからないことが挙げられます。クラスの定義とインスタンスの生成を合わせて行うことができるため、コードの記述がシンプルになるメリットがあり、簡潔でわかりやすいコードを書くことができます。
ラムダ式を使用することで、無名の関数を作成でき、関数を引数として渡したり、戻り値として返したりすることができます。
また、関数型インターフェースを実装するためのソースを手短に記載できます。
さらに、「Stream API」の引数としての関数型インターフェースを記述するのに適しています。
このようにコードをより効率的に記述することができるほか、コードの書き方が変わるため、アプリケーションのパフォーマンスも向上する場合もあります。
3. ラムダ式のデメリット

1.名前参照ができない
ラムダ式は名前参照ができません。
何度も使用したり再帰的に使用したりする場合は、通常の記述方法の方が分かりやすいコードを作成した方が良いです。
2.デバッグが難しい
ラムダ式の箇所が実行に失敗してしまった場合、スタックトレースから呼び出し元を特定することが困難になります。
複雑なラムダ式を記述すると、コードが読みにくくなることがあります。
3.学習コストがやや高い
簡潔に記載されているため、学習したての頃はどのような処理になっているのか理解しづらい点があると考えられます。
特に、Stream記述が連なっていると可読性としても逆にわかりづらくなってしまいます。
ただし、コーディングの作法に慣れてしまえばそこまで難しくはありません。
4.簡潔に記載されているため分かりづらい
学習コストがやや高い点にも繋がりますが、ラムダ式はぱっと見て何をしているのか分かりづらく、可読性が悪いという点があります。
ラムダ式が登場した際には、このような制限から可読性が落ちるという理由で、開発規則で禁止となる事例がありました。
これはデメリットというよりは、メリットが理解できずに実際の利用が進まなかったという方が正しいと考えられます。
しかし、ラムダ式は簡潔である反面、処理が複雑になるとコードの保守性が低下することがあるため、適切な場面でのみ使用することが望ましいと考えられます。
4. ラムダ式の構文

1.基本構文
ラムダ式の基本書式は以下になります。(メソッドの引数列) -> {処理内容}
2つの整数値を引数に取り、その和をメソッドに渡すケースは、ラムダ式で以下のように書くことができます。
(int x, int y) -> {return x + y;}
上記の例では引数の型宣言を行っていますが、以下のように省略することが可能になります。
(x, y) -> {return x + y;}
また、処理内容が1行の記述で済む場合、戻り値の中のかっこを省略してさらにシンプルにまとめることができます。
(x, y) -> return x + y;
2.「Stream API」の引数にラムダ式を使う構文
Java 8から新たに言語拡張された機能に「Stream API」があります。ラムダ式には、要素の直列処理や並列処理、要素の比較並び替え、一致判定などの処理を実行するメソッドが用意されています。
以下のように記述して使用します。
配列やList、Collectionなどのオブジェクト名.stream().メソッド名((引数) -> {処理})
・ラムダ式によるfileterの使い方
Integer[] num = {1, 2, 3, 4, 5};
List<Integer> l = Arrays.asList(num);
// ラムダ式の記述:3未満を抽出
l.stream().filter(x -> x < 3).forEach(System.out::println);
・ラムダ式によるsortedの使い方
Integer[] num = {1, 3, 5, 4, 2};
List<Integer> l = Arrays.asList(num);
// ラムダ式の記述:降順にソート
l.stream().sorted((x, y) -> y - x).forEach(System.out::println);
・ラムダ式によるmapの使い方
Integer[] num = {1, 2, 3, 4, 5};
List<Integer> l = Arrays.asList(num);
// ラムダ式の記述:四則演算
l.stream().map(x -> x * 10 - 5).forEach(System.out::println);
3.その他の構文
・ラムダ式によるforEachの使い方Integer[] num = {1, 2, 3, 4, 5};
List<Integer> l = Arrays.asList(num);
// ラムダ式の記述
l.forEach(System.out::println);
・ラムダ式によるメソッド参照一覧
構文 ー ラムダ式例 ー 従来記述例
クラス名::メソッド名 ー String::toString ー String.toString(引数)
オブジェクト名::メソッド名 ー System.out::println ー System.out.println(引数)
クラス名::new ー ArrayList::new ー new ArrayList()
・ラムダ式による引数の宣言部
パターン:引数部の書き方
抽象メソッドに引数がない場合:()
抽象メソッドの引数が1つだけの場合:(引数の型 引数の変数名)
:(引数の変数名)
 
5. まとめ
ラムダ式について学びました。最初はとっつきにくい文法でなにを行っているのかわからない処理に感じました。
しかし、ラムダ式の便利さについても知ることができました。
文法の記載についてやラムダ式について、この記事を読んだ方が少しでも理解していただけたら幸いです。