web-dev-qa-db-ja.com

Runnable :: new vs new Runnable()

次の例の最初の例が機能しないのはなぜですか?

  • 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コンストラクターが呼び出され、ラムダ(オブジェクトではありません)を返します。

しかし、例が正常にコンパイルされることはどのようにして可能ですか?

62
user1722245

より読みやすい答えを出すためにここでちょうど私の2セントです。人々はJavaラムダの世界に不慣れだからです。

これは何ですか

  • _R::new_は、Java8から登場した_method reference_を使用しており、既存のメソッド定義を再利用して、ラムダのように渡すことができます。したがって、_Runnable::new_を書くと、実際には_() -> new R()_を意味し、結果はラムダになります。
  • new R()はクラスRのコンストラクターを呼び出し、そのクラスのインスタンスを返します。結果はRのインスタンスです。

それらが何であるかは明確になりましたが、どのように機能するのですか(なぜコンパイル可能か)?

使い方

new R()の場合、何が起こったかを理解するのは非常に簡単なので、説明せずにそのままにしておきます。

_Runnable::new_を表す_() -> new R()_の場合、RunnableFunctionalInterfaceと呼ばれるものであり、関数型インターフェースはメソッドが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()を渡すことはできません。これは、問題が大した問題ではなく、魅力的な偶然であることを示しています。

0
Lebecca