メソッド参照について
Stremの振り返り
以前のStreamAPIの記事で、こんなコードを書きました。
IntStream.rangeClosed(1,30).boxed().map(value-> { String result = ""; result = value % 3 == 0 && value % 5 == 0 ? "FizzBuzz" : value % 3 == 0 ? "Fizz" : value % 5 == 0 ? "Buzz" : value.toString(); return result; }).forEach(System.out::println);
一番最後のこの部分(System.out::prnitln)
これは、メソッド参照と呼ばれる構文です。
ラムダ式の場合
関数型インターフェースに代入する際、ラムダ式だとこんな感じ。
public class Studay { public static void main(String[] args) { Function<Integer,String> funcHello = (num) -> "Hello" + num.toString(); System.out.println(funcHello.apply(1));//Hello1 } }
メソッド参照
ラムダ式でも十分簡潔ですが、もっと簡潔に書けるのがメソッド参照。 関数型インターフェースと引数、戻り値、型が一致していれば、メソッドを代入することができます。
public class Studay { public static void main(String[] args) { Function<Integer,String> funcHello = Studay::Hello; System.out.println(funcHello.apply(1)); } public static String Hello(Integer num){ return "Hello" + num.toString(); //Hello1 }; }
staticメソッドの場合は、クラス名::メソッド名。
非staticメソッドの場合は、インスタンス名::メソッド名で記述します。
最初の例に戻ると、forEachの部分でメソッド参照を使っています。
ドキュメントを読むと…
forEach
void forEach(Consumer<? super T> action)
このストリームの各要素に対してアクションを実行します。
Consumerってのを使ってますね。Consumerを調べてみると…。
インタフェースConsumer
型パラメータ:T - オペレーションの入力の型 @FunctionalInterface
public interface Consumer
単一の入力引数を受け取って結果を返さないオペレーションを表します。Consumerは他のほとんどの関数型インタフェースと異なり、副作用を介して動作することを期待されます。
これは、accept(Object)を関数メソッドに持つ関数型インタフェースです。
引数は1つ受け取るけど、戻り値はないようです。
ラムダ式だと、こんな使い方。
public class Studay { public static void main(String[] args) { Consumer<String> Hello = (str) -> System.out.println(str); Hello.accept("Hello");//Hello } }
System.out.println()は、引数を受け取って結果を返さずに標準出力を行うメソッドなので、Consumerと一致しています。
ってことで、これをメソッド参照で書くと、こんな感じ。
public class Studay { public static void main(String[] args) { Consumer<String> Hello = System.out::println; Hello.accept("Hello"); } }
ってことは、forEachはConsumerを期待してるので、System.out.printlnが使えそうです。
IntStream.rangeClosed(1,30).boxed().map(value-> { String result = ""; result = value % 3 == 0 && value % 5 == 0 ? "FizzBuzz" : value % 3 == 0 ? "Fizz" : value % 5 == 0 ? "Buzz" : value.toString(); return result; }).forEach(System.out::println);
もちろん、途中のmapはFunctionを期待してるので、mapの中身もメソッド化してしまえばメソッド参照ができます。
public class Studay { public static void main(String[] args) { IntStream.rangeClosed(1,30).boxed().map(Studay::FizzBuzz).forEach(System.out::println); } public static String FizzBuzz(Integer i){ String result = ""; result = i % 3 == 0 && i % 5 == 0 ? "FizzBuzz" : i % 3 == 0 ? "Fizz" : i % 5 == 0 ? "Buzz" : i.toString(); return result; } }
スッキリしましたね。
メソッド参照を使うと、よりコードを美しく書くことができそうです。
ただ、私のようなミジンコだと、引数の数やどんな処理をしているのかがぱっと見判断し辛いかも。慣れなきゃ…。メソッドの命名も重要だなぁ。