次の例の最初の例が機能しないのはなぜですか?
run(R::new);
メソッド_R.run
_は呼び出されません。run(new R());
メソッド_R.run
_isが呼び出されました。どちらの例もコンパイル可能です。
_public class ConstructorRefVsNew {
public static void main(String[] args) {
new ConstructorRefVsNew().run(R::new);
System.out.println("-----------------------");
new ConstructorRefVsNew().run(new R());
}
void run(Runnable r) {
r.run();
}
static class R implements Runnable {
R() {
System.out.println("R constructor runs");
}
@Override
public void run() {
System.out.println("R.run runs");
}
}
}
_
出力は次のとおりです。
_ R constructor runs
-----------------------
R constructor runs
R.run runs
_
最初の例では、R
コンストラクターが呼び出され、ラムダ(オブジェクトではありません)を返します。
しかし、例が正常にコンパイルされることはどのようにして可能ですか?
より読みやすい答えを出すためにここでちょうど私の2セントです。人々はJavaラムダの世界に不慣れだからです。
R::new
_は、Java8から登場した_method reference
_を使用しており、既存のメソッド定義を再利用して、ラムダのように渡すことができます。したがって、_Runnable::new
_を書くと、実際には_() -> new R()
_を意味し、結果はラムダになります。new R()
はクラスR
のコンストラクターを呼び出し、そのクラスのインスタンスを返します。結果はR
のインスタンスです。それらが何であるかは明確になりましたが、どのように機能するのですか(なぜコンパイル可能か)?
new R()
の場合、何が起こったかを理解するのは非常に簡単なので、説明せずにそのままにしておきます。
_Runnable::new
_を表す_() -> new R()
_の場合、Runnable
はFunctionalInterface
と呼ばれるものであり、関数型インターフェースはメソッドが1つだけのインターフェースであることを知る必要があります。関数インターフェイスをパラメーターとして受け入れるメソッドにラムダを渡す場合、ラムダはそのインターフェイスのシグネチャと一致する必要があり、ラムダのアクションはそのメソッドの本体に入力されます。
_JDK11
_のRunnable
は次のようになります。
_@FunctionalInterface
public interface Runnable {
public abstract void run();
}
_
ラムダ_() -> new R()
_はメソッドシグネチャと互換性がありますが、何も受け入れず何も返さないため、コードは機能します。この場合、渡されたパラメータのrunTimeオブジェクトは次のようになります。
_Runnable instance with run method {
public void run() {
new R();
};
}
_
これで、この状況でR
の構築のみがトリガーされた理由がわかります。
run(new R())
が次のようなラムダを使用して実行できることを実現できます。
_new ConstructorRefVsNew().run(() -> System.out.println("R.run runs"));
_
ラムダ_Runnable::new
_が実際にRunnable
インターフェースと互換性があるという質問が非常にトリッキーであることをようやく理解しました。あなたはFoo
と呼ばれるカスタマイズされた機能的なインターフェース、または同じことをするために何でも定義することができます
_@FunctionalInterface
public interface Foo{
public abstract void run();
}
public class ConstructorRefVsNew {
public static void main(String[] args) {
new ConstructorRefVsNew().run(R::new);
}
void run(Foo f) {
f.run();
}
}
_
この場合、_R::new
_は引き続きうまく機能しますが、new R()
を渡すことはできません。これは、問題が大した問題ではなく、魅力的な偶然であることを示しています。