threads thread run references pass parameter open new metodo method implementando example baeldung and java java-8 runnable constructor-reference

java - thread - Runnable:: nuevo vs nuevo Runnable()



threads en java 8 (4)

¿Por qué no funciona el primero de los siguientes ejemplos?

  • run(R::new); Método R.run no se llama.
  • run(new R()); Método se llama R.run .

Ambos ejemplos son 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 salida es:

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

En el primer ejemplo, se llama al constructor R , devuelve lambda (que no es un objeto):

Pero entonces, ¿cómo es posible que el ejemplo se compile con éxito?


Compara dos llamadas:

((Runnable)() -> new R()).run(); new R().run();

Por ((Runnable)() -> new R()) o ((Runnable) R::new) , crea un nuevo Runnable que no hace nada 1 .

Con la new R() , creas una instancia de la clase R donde el método de run está bien definido.

1 En realidad, crea un objeto de R que no tiene impacto en la ejecución.

Estaba pensando en tratar 2 invocaciones de manera idéntica sin modificar el método main . Necesitaríamos sobrecargar la run(Runnable) con la run(Supplier<Runnable>) .

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(); } void run(Supplier<Runnable> s) { run(s.get()); } static class R implements Runnable { ... } }


El método de run espera un Runnable .

El caso fácil es new R() . En este caso, usted sabe que el resultado es un objeto de tipo R R sí es ejecutable, tiene un método de run , y así es como lo ve Java.

Pero cuando pasas R::new algo R::new está sucediendo. Lo que le dice es que cree un objeto anónimo compatible con un Runnable cuyo método de run ejecuta la operación que le pasó.

La operación que pasaste no es el método de run R La operación es el costructor de R Por lo tanto, es como si le hubieras pasado una clase anónima como:

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

(No todos los detalles son iguales, pero esta es la construcción Java "clásica" más cercana).

R::new , cuando se llama, llama a new R() . Nada más y nada menos.



Su método de run toma una instancia de Runnable , y eso explica por qué run(new R()) funciona con la implementación de R

R::new no es equivalente a new R() . Puede ajustarse a la firma de un Supplier<Runnable> (o interfaces funcionales similares), pero R::new no se puede usar como Runnable implementado con su clase R

Una versión de su método de run que puede tomar R::new podría tener este aspecto (pero esto sería innecesariamente complejo):

void run(Supplier<Runnable> r) { r.get().run(); }

¿Por qué se compila?

Debido a que el compilador puede hacer un Runnable fuera de la llamada del constructor, y eso sería equivalente a esta versión de la expresión lambda:

new ConstructorRefVsNew().run(() -> { new R(); //discarded result, but this is the run() body });

Lo mismo se aplica a estas declaraciones:

Runnable runnable = () -> new R(); new ConstructorRefVsNew().run(runnable); Runnable runnable2 = R::new; new ConstructorRefVsNew().run(runnable2);

Pero, como puede observar, el Runnable creado con R::new simplemente llama a new R() en su cuerpo del método de run .

Un uso válido de una referencia de método para ejecutar R#run podría usar una instancia como esta (pero seguramente preferiría usar la instancia r directamente, en este caso):

R r = new R(); new ConstructorRefVsNew().run(r::run);