Runnable::new vs new Runnable()
Why doesn't the first of the following examples work?
-
run(R::new);
methodR.run
is not called. -
run(new R());
methodR.run
is called.
Both examples are compiled-able.
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");
}
}
}
The output is:
R constructor runs
-----------------------
R constructor runs
R.run runs
In the first example, the R
constructor is called, it returns lambda (which is not object):
But then how is it possible, that the example is compiled successfully?
Your run
method takes a Runnable
instance, and that explains why run(new R())
works with the R
implementation.
R::new
is not equivalent to new R()
. It can fit the signature of a Supplier<Runnable>
(or similar functional interfaces), but R::new
cannot be used as a Runnable
implemented with your R
class.
A version of your run
method that can takeR::new
could look like this (but this would be unnecessarily complex):
void run(Supplier<Runnable> r) {
r.get().run();
}
Why does it compile?
Because the compiler can make a Runnable
out of the constructor call, and that would be equivalent to this lambda expression version:
new ConstructorRefVsNew().run(() -> {
new R(); //discarded result, but this is the run() body
});
The same applies to these statements:
Runnable runnable = () -> new R();
new ConstructorRefVsNew().run(runnable);
Runnable runnable2 = R::new;
new ConstructorRefVsNew().run(runnable2);
But, as you can notice, the Runnable
created with R::new
does just call new R()
in its run
method body.
A valid use of a method reference to execute R#run
could use an instance, like this (but you'd surely rather use the r
instance directly, in this case):
R r = new R();
new ConstructorRefVsNew().run(r::run);
The first example:
new ConstructorRefVsNew().run(R::new);
is more or less equivalent to:
new ConstructorRefVsNew().run( () -> {new R();} );
The effect is you just create an instance of R but do not call its run
method.
Compare two calls:
((Runnable)() -> new R()).run();
new R().run();
By ((Runnable)() -> new R())
or ((Runnable) R::new)
, you create a new Runnable
which does nothing1.
By new R()
, you create an instance of the R
class where the run
method is well-defined.
1 Actually, it creates an object of R
which has no impact on execution.
I was thinking of treating 2 invocations identically without modifying the main
method. We would need to overload run(Runnable)
with 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 { ... }
}
The run
method expects a Runnable
.
The easy case is new R()
. In this case you know the result is an object of type R
. R
itself is a runnable, it has a run
method, and that's how Java sees it.
But when you pass R::new
something else is happening. What you tell it is to create an anonymous object compatible with a Runnable
whose run
method runs the operation you passed it.
The operation you passed it is not R
's run
method. The operation is the costructor of R
. Thus, it's like you have passed it an anonymous class like:
new Runnable() {
public void run() {
new R();
}
}
(Not all the details are the same, but this is the closest "classical" Java construct ).
R::new
, when called, calls new R()
. Nothing more, nothing less.