はじめに
これまでの案件で、比較的古いバージョンのJavaに触れることが多く、
ラムダ式に馴染みがありませんでした。
しかし、とある案件で実装中にラムダ式を使う場面があり、最初は->など馴染みのない記号があり、苦手意識がありました。そこで、今回はラムダ式の基礎的な部分について、初見で引っかかった点や、調べながら実装した経験を備忘録として整理してみます。
ラムダ式とは
ラムダ式とは、簡単にいうと「抽象メソッドが1つだけのインターフェース(関数型インターフェース)」に対して、処理を簡潔に記述するための書き方です。
従来の書き方と比較すると、以下のように記述を省略できます。
※インターフェースクラスは以下を準備します。
public interface MessageOutput {
void print(String message);
}
⚪︎従来の書き方
public class LambdaTest {
public static void main(String[] args) {
MessageOutput output = new MessageOutput() {
@Override
public void print(String message) {
System.out.println("メッセージ: " + message);
}
};
output.print("ラムダTEST");
}
}
⚪︎ラムダ式(省略した記法)
無名クラスで実装していた処理のうち、「メソッド定義部分」が省略され、
処理内容だけを簡潔に書けるようになっています。
※無名クラスとは、クラス名を定義せずにその場でクラスを実装する書き方です。
一度しか使わない処理などで、別途クラスを作成する手間を省くために使用されます。
public class LambdaTest {
public static void main(String[] args) {
MessageOutput output = message -> System.out.println("メッセージ: " + message);
output.print("ラムダTEST");
}
}
引っかかった点と分かった点
初めてラムダ式を見た時、以下の点に引っかかりました。
1.引数の書き方
2.-> の意味
3.なぜこの記法だけで動くのか。
特に2.の -> は「左側が引数、右側が処理」という意味になっているものが、
最初はただの記号として見えてしまい、どのように読み取ればよいのか分かりませんでした。
そこで実際のコードを分解して考えてみました。
message -> System.out.println(message);
① message(引数)
② ->(引数と処理をつなぐ記号)
③ System.out.println(message)(実行される処理)
このように、「messageを受け取って、その内容を出力する処理」と読むことで、
少しずつ理解しやすくなりました。
また、従来の無名クラスはメソッド名や型を含めて記述していましたが、
ラムダ式では実装するメソッドが1つに決まっているため、
それらを省略して処理内容だけを書くことができます。どのメソッドを実装するかが明確なため、「どの処理か」を書く必要がなく、「何をするか」だけに集中して記述できる点が便利な点です。
この、「前提があるから省略できる」という点が、最初は少し分かりづらかったです。
※なお、ラムダ式では、引数の数によって以下の通り、書き方が少し変わります。
▫️引数なし
引数がない場合は () のみで記述します。
() -> System.out.println("ラムダTEST")
▫️引数1つ
引数が1つの場合は () を省略することができます。
message -> System.out.println(message)
▫️引数2つ
引数が2つ以上の場合は () が必須となります。
(a, b) -> a.compareTo(b)
実際に試したこと
実際の現場の中で、一覧データの処理を行う際に、ラムダ式を使用する場面がありました。
例えば、工事機器名のリストに対して、「機器名を名前順に1件ずつ出力する」といった処理です。
従来の書き方では、以下のように、for文で書くことが多かったです。
⚪︎従来の書き方
List<String> equipmentNames = Arrays.asList("変圧器", "遮断器", "ケーブル");
// ソート処理
equipmentNames.sort(new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
});
// 出力
for (String equipmentName : equipmentNames) {
System.out.println(equipmentName);
}
⚪︎ラムダ式
コード量が減り、意図が分かりやすくなったと思います。
List<String> equipmentNames = Arrays.asList("変圧器", "遮断器", "ケーブル");
// ソート処理
equipmentNames.sort((a, b) -> a.compareTo(b));
// 出力
equipmentNames.forEach(equipmentName -> System.out.println(equipmentName));
まとめ
初めて見た時は->などがただの記号に見えて抵抗がありました。しかし実際に調べて分解してみてみると「記述が簡潔になることもある」ということに気づきました。ただ、「短く書けること」と「読みやすいこと」は必ずしも同じではないと思いました。慣れていない段階では、逆に処理の意図が追いづらく感じることもありましたので、使い所を意識する事が大事であると感じました。また、これまで触れたことがないこともあり、多少学習コストがかかるとも感じました。
今回は基本的な部分しか触れていませんでしたが、ラムダ式と組み合わせて利用できるAPIが数多く備わっているため、今後はそれらも含め、理解を深めていけたらと思います。