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?
Juste mes deux cents ici pour donner une réponse plus lisible, car les gens sont nouveaux dans Java monde lambda.
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)?
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.
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.