web-dev-qa-db-fra.com

Runnable :: new vs new Runnable ()

Pourquoi le premier des exemples suivants ne fonctionne-t-il pas?

  • run(R::new); méthode R.run n'est pas appelée.
  • run(new R()); méthode R.run est appelée.

Les deux exemples sont compilables.

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");
      }
  }
}

La sortie est:

  R constructor runs
  -----------------------
  R constructor runs
  R.run runs

Dans le premier exemple, le constructeur R est appelé, il retourne lambda (qui n'est pas un objet):

Mais alors comment est-il possible que l'exemple soit compilé avec succès?

62
user1722245

Juste mes deux cents ici pour donner une réponse plus lisible, car les gens sont nouveaux dans Java monde lambda.

Qu'est-ce que c'est ça

  • R::new Utilise method reference, Qui a émergé de Java8, vous permet de réutiliser les définitions de méthode existantes et de les passer comme des lambdas. Ainsi, lorsque vous écrivez Runnable::new, Cela signifie en fait () -> new R(), Le résultat est un lambda;
  • new R() appelle le constructeur de la classe R et retourne une instance de cette classe, le résultat est une instance de R.

Nous savons maintenant de quoi il s'agit, mais comment ils fonctionnent (pourquoi ils sont compilables)?

Comment ça fonctionne

Pour new R(), il est très facile de comprendre ce qui s'est passé et je pars sans explication.

Pour Runnable::new Qui signifie () -> new R(), Nous devons savoir que Runnable est ce que nous avons appelé le FunctionalInterface, et une interface fonctionnelle est une interface avec une seule , lorsque nous transmettons lambda à une méthode qui accepte une interface fonctionnelle comme paramètre, la lambda doit correspondre à la signature de cette interface, et l'action dans lambda est remplie jusqu'au corps de cette méthode.

Le Runnable dans JDK11 Ressemble à ceci:

@FunctionalInterface
public interface Runnable {

    public abstract void run();
}

tandis que le lambda () -> new R() est compatible avec la signature de la méthode - n'acceptez rien et ne retournez rien, donc le code fonctionne, et dans ce cas, l'objet runTime du paramètre passé ressemble à ceci:

Runnable instance with run method {

    public void run() {
      new R();
    };
}

maintenant nous savons pourquoi dans cette situation, seule la construction de R s'est déclenchée.

Quoi de plus

Nous pouvons réaliser ce que run(new R()) fait avec lambda comme ceci:

new ConstructorRefVsNew().run(() -> System.out.println("R.run runs"));

Je réalise enfin que la question est très délicate: le lambda Runnable::new Donné est en fait compatible avec l'interface Runnable. Vous pouvez définir une interface fonctionnelle personnalisée appelée Foo ou quoi que ce soit pour faire la même chose

@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();
  }
}

et dans ce cas, le R::new fonctionne toujours bien tandis que le new R() ne peut pas être transmis, ce qui indique que la question n'est pas un gros problème mais une coïncidence fascinante.

0
Lebecca