あいぼーいの部屋

@I_BOY_1204がJavaやJavaScriptに関して調べた事を忘れない様につらつらと記述する備忘録です。

Java8 Method Reference

Java8からLambdaと一緒にMethod Referenceも導入されました。

これにより、今までアノニマスクラスで書いていたものが、LambdaやMethod Referenceで書くことができるようになりました。
例えば、Comparatorの実装は、

Comparator<String> comparator = String::compareTo;

と書くことができるようになりました。

ここで、気になるのは、Method Referenceでは、どんなものがあって、どういう風に書けるのかということ。
Method Referenceには大きく分けて、4種類のものがあるようです。

  • スタティックメソッドのMethod Reference
  • 任意の型指定によるインスタンスメソッドのMethod Reference
  • オブジェクト指定のインスタンスメソッドのMethod Reference
  • コンストラクタのMethod Reference

書き方については、4種類すべて同じで、

ClassName::methodName

という書き方をしますが、4種類それぞれで、引数とメソッドが呼び出される形式が違います。
具体的にはそれぞれ、以下のような使い方をします。

スタティックメソッドのMethod Reference

引数がそのまま、スタティックメソッドの引数に割り当てられます。
これに対応するLambdaは以下のようになります。

(arg) -> ClassName.staticMethod(arg);
任意の型指定によるインスタンスメソッドのMethod Reference

第一引数に対して、Method Referenceで与えたメソッドが呼び出される形となります。
そのため、第一引数とMethod Referenceの型が一致しない場合はコンパイルエラーになります。
残りの引数は、インスタンスメソッドの引数として渡されます。

対応するLambdaは以下のようになります。

(arg1, arg2, arg3) -> arg1.instanceMethod(arg2, arg3);

ちなみに、親クラスを指定した場合は、

super::methodName

と書くことができます。
また、ジェネリクスなどで型を明示的に指定したい場合は

ClassName::<Type>methodName

と書くこともできます。

オブジェクト指定のインスタンスメソッドのMethod Reference

こちらは、スタティックメソッドのMethod Referenceと同様に、
すべての引数がそのままインスタンスメソッドの引数に割り当てられます。

これに対応するLambdaは以下のようになります。

CustomClass instance = new CustomClass();
(arg1, arg2) -> instance.instanceMethod(arg1, arg2);

ここで、Method ReferenceとLambdaの違いは、Lambdaで実行する場合、
instanceはfinalもしくはeffectively finalである必要がありますが、Method Referenceではそのような制約はないということです。

その他、以下のような操作も書くことができます。

Supplier supplier = (list.size() > 10 ? list.subList(0,10) : list)::iterator;

ちょっと読み難いですが、ちゃんと動きます。

コンストラクタのMethod Reference

これも基本的には、スタティクメソッドの場合と同じです。
すべての引数がそのままコンストラクタの引数に割り当てられます。
ただし、注意が必要なのは、メソッド名としてnewを使う点です。

そのため、コンストラクタのMethod Referenceだけは以下のような形になります。

ClassName::new;

ちょっと触ってみた感想としては、Lambdaに比べて、引数の関係が直感的でないせいか、
読むのはいいですが、書くのに最初は苦労する感じがしました。(慣れれば問題なさそう)
ただ、便利だとは思うので、可読性を下げない程度に使っていきたいと思います。