1. 概要

Java 8では、メソッド参照の概念が導入されました。 それらはラムダ式に似ているとよく見られます。

ただし、メソッド参照とラムダ式はまったく同じものではありません。 この記事では、それらが異なる理由と、それらを間違った方法で使用することのリスクについて説明します。

2. ラムダとメソッドリファレンスの構文

まず、ラムダ式の例をいくつか見てみましょう。

Runnable r1 = () -> "some string".toUpperCase();
Consumer<String> c1 = x -> x.toUpperCase();

そして、メソッド参照のいくつかの例:

Function<String, String> f1 = String::toUpperCase;
Runnable r2 = "some string"::toUpperCase;
Runnable r3 = String::new;

これらの例は、メソッド参照をラムダの短縮表記として考えるようにさせることができます。

しかし、公式のOracleドキュメントを見てみましょう。 そこで興味深い例を見つけることができます:

(test ? list.replaceAll(String::trim) : list) :: iterator

ご覧のとおり、Java言語仕様では、二重コロン演算子の前に異なる種類の式を使用できます。 :: の前の部分は、 the targetreferenceと呼ばれます。

次に、メソッド参照評価のプロセスについて説明します。

3. メソッドリファレンス評価

次のコードを実行するとどうなりますか?

public static void main(String[] args) {
    Runnable runnable = (f("some") + f("string"))::toUpperCase;
}

private static String f(String string) {
    System.out.println(string);
    return string;
}

実行可能オブジェクトを作成しました。 それ以上でもそれ以下でもありません。 ただし、出力は次のとおりです。

some
string

これは、宣言が最初に検出されたときにターゲット参照が評価されるために発生しました。したがって、目的の怠惰が失われました。 ターゲット参照も1回だけ評価されます。したがって、上記の例にこの行を追加すると、次のようになります。

runnable.run()

出力は表示されません。 次のケースはどうですか?

SomeWorker worker = null;
Runnable workLambda = () -> worker.work() // ok
Runnable workMethodReference = worker::work; // boom! NullPointerException

前述のドキュメントによる説明:

「インスタンスメソッドを呼び出すメソッド呼び出し式(§15.12)は、ターゲット参照がnullの場合、NullPointerExceptionをスローします。」

予期しない状況を防ぐ最善の方法は、ターゲット参照として変数アクセスと複雑な式を使用しないことです

メソッド参照は、ラムダに相当するものの簡潔で短い表記としてのみ使用することをお勧めします。 :: 演算子の前にクラス名を付けるだけで、安全性が保証されます。

4. 結論

この記事では、メソッド参照の評価プロセスについて学びました。

アプリケーションの動作に突然驚かないようにするために従うべきリスクとルールを知っています。