问题
I have the following class with an overloaded method:
import java.util.ArrayList;
import java.util.concurrent.Callable;
public abstract class Test {
public void test1 () {
doStuff (ArrayList::new); // compilation error
}
public void test2 () {
doStuff ( () -> new ArrayList<> ());
}
public abstract void doStuff (Runnable runable);
public abstract void doStuff (Callable<ArrayList<String>> callable);
}
The method test1
results in a compilation error with the error message
The method doStuff(Runnable) is ambiguous for the type Test
.
I've added a third method test3
which looks like this:
public void test3 () {
doStuff ( () -> {
new ArrayList<> ();
});
}
Here the method doStuff(Runnable)
is executed which is obvious.
But how does the compiler decide which of the two methods is executed in test2
?
Why can I use the lambda expression but not the method reference?
The lambda expression in test2
useses the method which returns the callable, why does the method reference try to use the other method?
This seems to me like a java bug.
Edit:
It has nothing to do with the ArrayList
and/or the generic type of it. Same error when you have Callable<String>
or any other object.
Thanks in advance
Dimitri
回答1:
Well, we can simplify this:
// takes a Runnable
public static void doStuff(Runnable runable) {
System.out.println("Runnable");
}
// takes a Callable
public static void doStuff(Callable<List<String>> callable) {
System.out.println("Callable");
}
And two extra methods that are overloads.
private static List<String> go() {
return null;
}
private static List<String> go(int i) {
return null;
}
What do you think will happen if you call this:
doStuff(YourClass::go);
Yeah... this will fail to match. And you might think that this is stupid as it only makes sense that go
is the one that takes no arguments, it is easy for you in this simple situation to make this judgment, it's not for the compiler. In essence this is like a dead-lock:
In order to know which doStuff
method to call, we need to know which go
to call; and at the same time to understand which go
to call we need to know which doStuff
to call, or:
we need to resolve the method in order to find the target type, but we need to know the target type in order to resolve the method.
Same thing happens in your case with ArrayList
having more than one constructors...
回答2:
But how does the compiler decide which of the two methods is executed in test2?
public void test2 () {
doStuff ( () -> new ArrayList<> ());
}
is implicitly
public void test2 () {
doStuff ( () -> { return new ArrayList<>(); } );
}
and only Callable returns an object.
Why can I use the lambda expression but not the method reference?
It could be
public void test2 () {
doStuff ( () -> { new ArrayList<>(); } );
}
回答3:
EDITED
Look at those examples:
Runnable r = ArrayList::new; // compiled
Callable c = ArrayList::new; // compiled
doStuff(ArrayList::new); // compile error, ambiguous
So ArrayList::new
is interpreted as both Runnable
and Callable
. Note there is no lambda involved.
Next example:
Runnable r = () -> new ArrayList<>(); // compiled
Callable c = () -> new ArrayList<>(); // compiled
doStuff(() -> new ArrayList<>()); // compiled, the Callable one
When passing () -> new ArrayList<>()
to a method
() -> { return new ArrayList<>(); }
is preferred than
() -> { new ArrayList<>(); }
So, the Callable
one is invoked and there is nothing ambiguous.
来源:https://stackoverflow.com/questions/51710250/java-compile-error-method-reference-in-combination-with-overloading