私は現在、ラムダやメソッド参照などのJava 8
機能について詳しく調べています。少し遊んでみると、次の例にたどり着きました。
public class ConsumerTest {
private static final String[] NAMES = {"Tony", "Bruce", "Steve", "Thor"};
public static void main(String[] args) {
Arrays.asList(NAMES).forEach(Objects::requireNonNull);
}
}
私の質問は:
Mainメソッド内の行がコンパイルされるのはなぜですか?
私がそのことを正しく理解した場合、参照されるメソッドのシグネチャは、機能インターフェイスのSAMシグネチャに対応している必要があります。この場合、コンシューマーには次の署名が必要です。
void accept(T t);
ただし、requireNonNull
メソッドはvoidではなくT
を返します。
public static <T> T requireNonNull(T obj)
Java言語仕様バージョン8は 15.13.2 で述べています:
メソッド参照式は、Tが機能インターフェイスタイプ( §9.8 )であり、式が次の関数タイプと一致する場合、割り当てコンテキスト、呼び出しコンテキスト、またはキャストコンテキストでターゲットタイプTと互換性があります。 Tから派生した地上ターゲットタイプ。
[..]
次の両方が当てはまる場合、メソッド参照式は関数型と合同です。
- 関数型は、参照に対応する単一のコンパイル時宣言を識別します。
- 次のいずれかに該当します:
- 関数型の結果は無効です。
- 関数型の結果はRであり、キャプチャ変換( §5.1.1 )を呼び出し型の戻り値の型( §15.12.2.6 )に適用した結果です。選択されたコンパイル時宣言のはR '(RはR'を推測するために使用できるターゲットタイプ)であり、RもR 'も無効ではなく、R'は割り当てコンテキストでRと互換性があります。
(私の強調)
したがって、関数型の結果が無効であるという事実は、それを一致させるのに十分です。
JLS 15.12.2.5 は、メソッドを照合する際のvoidの使用についても具体的に言及しています。ラムダ式の場合、 15.12.2.1 で参照されるvoid-compatibleブロック( 15.27.2 )の概念があります。 =ですが、メソッド参照に相当する定義はありません。
より具体的な説明を見つけることができませんでした(ただし、JLSはクラックするのが難しいので、関連するセクションがいくつか欠落している可能性があります)が、これは、非呼び出しも許可されているという事実に関係していると思います-それ自体のステートメントとしてのvoidメソッド(割り当てなし、return
など))。
JLS 15.13.3メソッド参照の実行時評価 も次のように述べています。
コンパイル時の結果を判別するために、メソッド呼び出し式は、呼び出しメソッドの結果がvoidの場合は式ステートメントであり、呼び出しメソッドの結果が非voidの場合はreturnステートメントの式です。
メソッド参照のコンパイル時宣言がシグネチャ多態性である場合のこの決定の効果は次のとおりです:
- メソッド呼び出しのパラメーターのタイプは、対応する引数のタイプです。
- メソッドの呼び出しを囲む呼び出しメソッドが無効であるか戻り値の型であるかに応じて、メソッドの呼び出しは無効であるか、戻り値の型がObjectです。
したがって、生成されたメソッド呼び出しは、機能タイプに一致するように無効になります。
@Mark Rotteveelの正確な回答にあることとは別に、次の例のように、Javaでは、メソッド呼び出しの結果を無視できるため、コンパイルされます。
_Map<String, String> map = new HashMap<>();
map.put("1", "a");
map.put("1", "A"); // Who cares about the returned value "a"?
_
forEach()
コンシューマーのブロックには何も返さないため、仕様に従って有効です。