lambdaについて調べてみた その2

【概要】

前回はラムダの基本的な書き方を調べ、
インターフェイスシンタックスシュガーに
似た動きをするのがわかりました。
(スコープの扱いが違うらしいですが、まだ試していません)

前回は省略できる部分を省略せずかいていたので、
今回は省略した書き方について調べて見ました。

【事前準備】

今回のサンプルを実行する準備として
↓のメソッドを作成しています。

    private static void execConsumer(final Consumer<String> consumer) {
        System.out.println("========== consumer start ==========");
        consumer.accept("consumer test");
        System.out.println("========== consumer end ==========");
        System.out.println("");
    }

    private static void execFunction(final Function<String, String> function) {
        
        System.out.println("========== function start ==========");
        System.out.println(function.apply("function test"));
        System.out.println("========== function end ==========");
        System.out.println("");
    }

毎回、変数に突っ込んで実行するのを書くのが面倒なので、
インターフェイスを受け取って実行するメソッドを作りました。

見た目がわかりやすいように余計な出力を実行していますが。

【省略していく】

[パラメータあり、戻り値なし]

まずはパラメータあり、戻り値なしのパターンの
書き方について見ていきます。

    execConsumer(  (String p) -> { System.out.println(p); }  );// No.1
    execConsumer(  (String p) ->   System.out.println(p)     );// No.2
    execConsumer(         (p) ->   System.out.println(p)     );// No.3
    execConsumer(          p  ->   System.out.println(p)     );// No.4

No.1は省略なしの書き方で、パラメータの型と名前を括弧でくくって、
実行するステートメントセミコロンつけて、全体を中括弧でくくって・・・
省略しててコンパイルエラーで困ったとときに
この書き方に立ち戻ってエラーの原因探すといいと思います。

No.2はNo.1から中括弧とステートメントセミコロンをはずしました。
実行するステートメントが1つの場合、ステートメントのみの記述で実行できます。

No.3はNo.2からパラメータの型の指定をはずしています。
今回のサンプルのexecConsumerのパラメータはConsumerです。
ジェネリクスで指定した型が適用されるためパラメータの型が不要です。

    final Consumer<String> consumer = (p) -> System.out.println(p);

↑の書き方と同じです。
と書いているので、パラメータpの型はStringとして扱われます。

No.4はパラメータの指定から括弧を外しています。

ここでメソッド参照と言う新しい機能を使ってみます。

    execConsumer(System.out::println);

パラメータの指定もなくなりました。
「オブジェクト::メソッド名」とやると
ラムダで受け取るパラメータをそのままメソッドに渡してくれます。

最後にステートメントが複数ある場合です。

    execConsumer(p -> {
        System.out.println(p);
        System.out.println(p);
    });

ステートメントが複数ある場合、省略できるのはパラメータの型と括弧だけです。

[パラメータあり、戻り値あり]

次にパラメータあり、戻り値ありのパターンの
書き方について見ていきます。

    execFunction(  (String p) -> { return "p : " + p; }  );//No.1
    execFunction(          p  -> { return "p : " + p; }  );//No.2
    execFunction(          p  ->          "p : " + p     );//No.3

    // No.4
    execFunction(p -> {
        System.out.println("test");
        return "p : " + p;
    });

No.1は省略なしの書き方です。
パラメータあり、戻り値なしのパターンと同じです。
こちらは戻り値があるのでreturnを使っています。

No.2はNo.1からパラメータの指定を変数名のみにしました。

No.3はreturnと括弧を省略しています。
ステートメントが1つだけの場合はreturnも省略できます。

No.4はステートメントが複数ある場合です。

[メソッド参照]

先ほど使ったメソッド参照で参照できるのは
staticメソッド、インスタンスメソッド、コンストラクタです。
(公式ドキュメントでは4種類です。インスタンスメソッドについては2種類あります。)

っで、まずは準備として適当なstaticメソッドを作ります。

public class Sample {
    private static String sampleMethod(final String value) {
        return value + " - " + value;
    }
}

Stringを取って、Stringを返します。

では、メソッド参照を使ってみます。

    execFunction(Sample::sampleMethod);//No.1

    execFunction("a"::concat);//No.2
    execFunction(String::toUpperCase);//No.3
    execFunction(p -> p.toUpperCase());//No.3-1

    execFunction(String::new);//No.4

No.1は作成したstaticメソッドを参照しています。

No.2は特定のオブジェクト(今回は"a")のメソッドに
ラムダで受け取ったパラメータを渡して実行しています。

No.3はNo.2と同じくインスタンスメソッドを使用していますが、
ラムダで受け取ったパラメータのインスタンスメソッドを実行しています。
No.3とNo.3-1は同じ結果になります。

No.4はコンストラクタを実行しています。

【まとめ】

省略した書き方について調べて見ましたが、
段階的に省略していけば結構すぐ慣れます。

StreamAPIを使う場合には省略した書き方を
多用することになると思うので、
慣れておかないと読めなくなりそうですね。