Why is this Java method call considered ambiguous? - java

I've come across a strange error message that I believe may be incorrect. Consider the following code:
public class Overloaded {
public interface Supplier {
int get();
}
public interface Processor {
String process(String s);
}
public static void load(Supplier s) {}
public static void load(Processor p) {}
public static int genuinelyAmbiguous() { return 4; }
public static String genuinelyAmbiguous(String s) { return "string"; }
public static int notAmbiguous() { return 4; }
public static String notAmbiguous(int x, int y) { return "string"; }
public static int strangelyAmbiguous() { return 4; }
public static String strangelyAmbiguous(int x) { return "string"; }
}
If I have a method that looks like this:
// Exhibit A
public static void exhibitA() {
// Genuinely ambiguous: either choice is correct
load(Overloaded::genuinelyAmbiguous); // <-- ERROR
Supplier s1 = Overloaded::genuinelyAmbiguous;
Processor p1 = Overloaded::genuinelyAmbiguous;
}
The error we get makes perfect sense; the parameter to load() can be assigned to either, so we get an error that states the method call is ambiguous.
Conversely, if I have a method that looks like this:
// Exhibit B
public static void exhibitB() {
// Correctly infers the right overloaded method
load(Overloaded::notAmbiguous);
Supplier s2 = Overloaded::notAmbiguous;
Processor p2 = Overloaded::notAmbiguous; // <-- ERROR
}
The call to load() is fine, and as expected, I cannot assign the method reference to both Supplier and Processor because it is not ambiguous: Overloaded::notAmbiguous cannot be assigned to p2.
And now the weird one. If I have a method like this:
// Exhibit C
public static void exhibitC() {
// Complains that the reference is ambiguous
load(Overloaded::strangelyAmbiguous); // <-- ERROR
Supplier s3 = Overloaded::strangelyAmbiguous;
Processor p3 = Overloaded::strangelyAmbiguous; // <-- ERROR
}
The compiler complains that the call to load() is ambiguous (error: reference to load is ambiguous), but unlike Exhibit A, I cannot assign the method reference to both Supplier and Processor. If it were truly ambiguous, I feel I should be able to assign s3 and p3 to both overloaded parameter types just as in Exhibit A, but I get an error on p3 stating that error: incompatible types: invalid method reference. This second error in Exhibit C makes sense, Overloaded::strangelyAmbiguous isn't assignable to Processor, but if it isn't assignable, why is it still considered ambiguous?
It would seem that the method reference inference only looks at the arity of the FunctionalInterface when determining which overloaded version to select. In the variable assignment, arity and type of parameters are checked, which causes this discrepancy between the overloaded method and the variable assignment.
This seems to me like a bug. If it isn't, at least the error message is incorrect, since there is arguably no ambiguity when between two choices only one is correct.

Your question is very similar to this one.
The short answer is:
Overloaded::genuinelyAmbiguous;
Overloaded::notAmbiguous;
Overloaded::strangelyAmbiguous;
all these method references are inexact (they have multiple overloads). Consequently, according to the JLS §15.12.2.2., they are skipped from the applicability check during overload resolution, which results in ambiguity.
In this case, you need to specify the type explicitly, for example:
load((Processor) Overloaded::genuinelyAmbiguous);
load(( Supplier) Overloaded::strangelyAmbiguous);

Method references and overloading, just... don't. Theoretically, you are more than correct - this should be fairly easy for a compiler to deduce, but let's not confuse humans and compilers.
The compiler sees a call to load and says : "hey, I need to call that method. Cool, can I? Well there are 2 of them. Sure, let's match the argument". Well the argument is a method reference to an overloaded method. So the compiler is getting really confused here, it basically says that : "if I could tell which method reference you are pointing to, I could call load, but, if I could tell which load method you want to call, I could infer the correct strangelyAmbiguous", thus it just goes in circles, chasing it's tale. This made up decision in a compilers "mind" is the simplest way I could think to explain it. This brings a golden bad practice - method overloading and method references are a bad idea.
But, you might say - ARITY! The number of arguments is the very first thing a compiler does (probably) when deciding if this is an overload or not, exactly your point about:
Processor p = Overloaded::strangelyAmbiguous;
And for this simple case, the compiler could indeed infer the correct methods, I mean we, humans can, should be a no brainer for a compiler. The problem here is that this is a simple case with just 2 methods, what about 100*100 choices? The designers had to either allow something (let' say up to 5*5 and allow resolution like this) or ban that entirely - I guess you know the path they took. It should be obvious why this would work if you would have used a lambda - arity is right there, explicit.
About the error message, this would not be anything new, if you play enough with lambdas and method references, you will start to hate the error message : "a non static method cannot be referenced from a static context", when there is literally nothing to do with that. IIRC these error messages have improved from java-8 and up, you never know if this error message would improve also in java-15, let's say.

Related

Why is this the program's output?

public class OverloadingTest extends Format{
public int add(String s1){
System.out.println("With String");
return 1;
}
public int add(Object a){
System.out.println("With Object");
return 1;
}
public static void main(String[] args) {
OverloadingTest overloadingTest = new OverloadingTest();
overloadingTest.add(null);
}
}
Why is the output of the program With String ?
I have tried reading JLS for 6th Version, but I still could not find the answer.
The only reason I could guess is that the closest match in Inheritance hierarchy is chosen.
So in this case it would take String as Object is its super class.
This answer is available in the question Method Overloading for NULL parameter:
Java will always try to use the most specific applicable version of a method that's available (see JLS §15.12.2).
So, as a result, String is used, because it's the most specific type available. If more than one subtype exists, then you will have to cast null to the appropriate type to designate which function you want to run.
This is spelled out in §15.12.2.5. Choosing the Most Specific Method:
If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.
The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time type error.
In your example, add(Object) can handle anything that add(String) can. This makes the latter more specific. It is therefore the one that gets chosen to handle null.
If more than one member method is both accessible and applicable to a method invocation ... The Java programming language uses the rule that the most specific method is chosen.
See The Java Language Specification

Why is the return type of method not included in the method-signature?

Why does return type of method not included in signature?
An example
public void method1(String arg){...}
public String method1(String arg){...}
It will cause an error.
This is done because the compiler would not be able to figure out the overload in all contexts.
For example, if you call
String x = method1("aaa");
the compiler knows that you are looking for the second overload. However, if you call
method1("aaa");
like this, the compiler has no idea which one of the two methods you wanted to invoke, because it is OK to call a method returning String and discard the result. To avoid ambiguities like this, Java prohibits overloads that differ solely on the return type.
Since your question doesn't address any particular programming language in the title (I know it does in the tag) I'll share my recent experience with Swift.
In Swift function/method signature actually includes return type. So compiler throws an error only if you call this function/method without explicitly specifying return type, e.g.:
func some() -> Bool {
return true;
}
func some() -> Int {
return 1;
}
let valBool: Bool = some()
let valInt: Int = some()
// this doesn't work: Ambiguous use of 'some'
some()
On top of this Swift even makes it more interesting. It allows you to have 2 functions/methods with the same parameters and return types only if parameters names are different, e.g.:
func some(#foo: Bool) -> Bool {
return foo;
}
func some(#bar: Bool) -> Bool {
return bar;
}
some(foo: true)
some(bar: false)
Thus it gives you semantic differentiation in methods signature
UPD. Since Swift 2.0 external parameter name was changed and now you have to provide external and local names twice even if it's the same
func some(foo foo: Bool) -> Bool {
return foo;
}
func some(bar bar: Bool) -> Bool {
return bar;
}
some(foo: true)
some(bar: false)
You can't overload a method only on it's return type. It's simply illegal. Let's assume for a moment that overloading methods using return type would be legal and you defined two method1 methods. Now we want to call that which returns the String object:
String string = method1(sth);
The JVM theoretically would be able to recognize which method you inteded to call, but what about such call:
method1(sth);
As you can see both methods could be invoked and such operations is unambiguous. The JVM doesn't know which method it should call. This is why such overloading is forbidden.
Because it is impossible to resolve which of the overloaded methods should be called in a case like this:
public static void main(String... args) {
method1("aaa");
}
There are several points to consider when designing things like overload resolution.
Reasons to omit overloading on return type:
Simplify ignoring the function return value (like people often do with the error codes).
Makes program easier to digest for human readers. In particular, that is the reason in Python they do not have function overloads at all. (matter of taste)
C legacy. When language comes from C-family and designers do not consider something to be a big deal it is left as it has always been...
Reasons to add overloading on return type:
Make it difficult to ignore returned values. This may be convenient and saves some typing, but will definitely bite you one day.
Expressivness (of course as opposed to ease of digesting :)). Did you ever wanted to write things like int x = json.get("int_value"); float y = json.get("float_value");? In some languages (like C++) that is still possible to achieve with proxies and cast operators, but overload on return type would be so much easier.
Expressiveness. Every time you pass retun value as a reference to your function just to reuse its resources this could be an overload on return type (with like hidden parameter). Consider string s; getline(cin, s); vs string s = getline(cin);. And this is where expressiveness comes together with referential transparency, and eventually, ease of code digesting.
Now back to your question 'why?'. Since you were asking about Java, the answer is obviously because James valued reasons to omit return type overloads over the reasons to include them in the language.
I had this very same question myself at one point, and while I can see how the compiler wouldn't know which function to call when you don't assign the return value to a variable of the appropriate type, why throw the error at the function level? Why not at the point the function(s) are called? Basically, once you've committed to having the signature differ only by the return value, it's up to you to make sure you use them that way, and then, and only then, would the compiler complain. Of course, it may take some extra work to get this to work with a strict one-pass compiler, but I think it could fly.
you can call function as procedure: method1("arg"); where method1 is the second method in your list (String method1(String arg){}). Compiler then would be unable to distinguish it from the first one (void method1(String arg){}).
Compiler takes care of method binding. When ever it encounters methodName() it has to bind to some method definition, at that point it may not know the return type of method. So method return type is not included in the method signature. Compiler binds the method according to the method signature.
When compiler encounters the method call. It statically binds the method call to one of the methods defined.
Lets see what happens if the return type is included in the method signature
class Example{
public void method1(String arg){ return arg}
public String method1(String arg){}
public static void main(String[] args){
Example e = new Example();
e.method1("abc");
}
}
e.method1("abc") The compiler will not know which method to bind to if return type is included in the method signature.
Method overloading is checked on the basis of number and type of arguments not on the basis of return type. That's why you are getting the error.

Overloading method calls with parameter null [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Method Overloading for NULL parameter
In the code below the output is
String
and if I remove the method with the parameter of type String then the output is
Object
I know how overloading of methods acts when the parameter types don't match exactly but I can not understand how null can be treated as an Object and/or a String parameter.
What is the explanation for this?
class C {
static void m1(Object x) {
System.out.print("Object");
}
static void m1(String x) {
System.out.print("String");
}
public static void main(String[] args) {
m1(null);
}
}
It always uses the most specific method according to the Java specs, section 15.12.2.5.
The intro is reasonably specific about it:
If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.
The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time type error.
Generally speaking, and at least for code readability, it's always best to try to be as explicit as possible. You could cast your null into the type that matches the signature you want to call. But that's definitely a questionable practice. It assumes everyone knows this rule and makes the code more difficult to read.
But it's a good academic question, so I +1 your question.
When multiple overloads match a signature, Java picks the most specific method from among them.
The value of null matches both Object and String, but String is a subclass of Object, so String is picked. If you add another overload with a sibling of String in the class hierarchy, you'd get a compile error.\
// DOES NOT COMPILE
class C {
static void m1(Object x) {
System.out.print("Object");
}
static void m1(String x) {
System.out.print("String");
}
static void m1(Integer x) {
System.out.print("Integer");
}
public static void main(String[] args) {
m1(null);
}
}
Here is a link to a post that discusses your code example at some length.
If you need to force the call of a aprticular overloaded method when passing null as parameter, you have to cast it, like this:
m1((String)null);
By doing this, you make sure you're calling the correct overloaded version of the method.
You have to set the type of null to tell Java what function you want to call.
So you do: m1((Object) null); to call the implementation with the Object parameter and you do m1((String) null); to call the other one.
1. As String is also an object the JVM got confused to call which method at runtime.
2. If you want to specify which method to call at runtime, you can do this by explicit casting
eg:
m1((String)null);

Reflection Casting and Overloaded Method Dispatching in Java

Note that all the code is a simplified example in order to only communicate the core ideas of my question. It should all compile and run though, after slight editing.
I have several classes which all implement a common interface.
public interface Inter{}
public class Inter1 implements Inter{}
public class Inter2 implements Inter{}
In a separate class I have a list of type Inter, which I use to store and remove Inter1 and Inter2 types, based on user input.
java.util.ArrayList<Inter> inters = new java.util.ArrayList<Inter>();
I also have a family of overloaded methods, which deal with how each implementation interacts with each other, along with a default implementation for 2 "Inter"s.
void doSomething(Inter in1, Inter in2){
System.out.println("Inter/Inter");
}
void doSomething(Inter1 in1, Inter1 in2){
System.out.println("Inter1/Inter11");
}
void doSomething(Inter2 in1, Inter1 in2){
System.out.println("Inter2/Inter1");
}
The methods are periodically called like so:
for(int i = 0; i < inters.size() - 1; i++){
for(int o = i+1; o < inters.size(); o++){
Inter in1 = inters.get(i); Inter in2 = inters.get(o);
doSomething(in1.getClass().cast(in1), in2.getClass().cast(in2));
System.out.println("Class 1: " + in1.getClass().getName());
System.out.println("Class 2: " + in2.getClass().getName());
}
}
An example output from this is:
Inter/Inter
Class 1: Inter
Class 2: Inter
Inter/Inter
Class 1: Inter
Class 2: Inter1
Inter/Inter
Class 1: Inter1
Class 2: Inter1
Looking at the output, it is clear that doSomething(Inter in1, Inter in2) is called, even in cases when other methods should be called. Interestingly, the class names outputted are the correct ones.
Why does java have static method overloading when the class types are determined at runtime using reflection?
Is there any way to get Java to do this? I know I can use reflection and Class.getMethod() and method.invoke() to get the results I want, but it would be so much neater to do so with casting.
I realize that questions about similar concepts have been asked before, but while all of the answers were informative, none satisfied me.
Double dispatch looked like it would work, but that would mean reworking a lot of code, since I use this type of thing often.
It looks to me like we're talking about what's going on with:
doSomething(in1.getClass().cast(in1), in2.getClass().cast(in2));
Based on your surprise that the type that is being output is always Inter, it seems you're a little confused on what's going on here. In particular, you seem to think that in1.getClass().cast(in1) and in2.getClass().cast(in2) should be forcing a different overload because of their differing runtime type. However, this is wrong.
Method overload resolution happens statically. This means that it happens based on the declared types of the two arguments to the method. Since both in1 and in2 are both declared as Inter, the method chosen is obviously void doSomething(Inter in1, Inter in2).
The takeaway here is that in1 is declared as an Inter. This means that in1.getClass() is essentially the same as Inter.class for the purposes of static analysis -- getClass simply returns a Class<? extends Inter>. Therefore, the casts are useless, and you're only ever going to get the first overload.
The Java Language Specification (JLS) in section 15.12 Method Invocation Expression explains in detail the process that the compiler follows to choose the right method to invoke.
There, you will notice that this is a compile-time task. The JLS says in subsection 15.12.2:
This step uses the name of the method and the types of the argument expressions
to locate methods that are both accessible and applicable
There may be more than one such method, in which case the most specific one is chosen.
In your case, this means that since you are passing two objects of type Integer, the most specific method is the one that receives exactly that.
To verify the compile-time nature of this, you can do the following test.
Declare a class like this and compile it.
public class ChooseMethod {
public void doSomething(Number n){
System.out.println("Number");
}
}
Declare a second class that invokes a method of the first one and compile it.
public class MethodChooser {
public static void main(String[] args) {
ChooseMethod m = new ChooseMethod();
m.doSomething(10);
}
}
If you invoke the main, the output says Number.
Now, add a second more specific method to the ChooseMethod class, and recompile it (but do not recompile the other class).
public void doSomething(Integer i) {
System.out.println("Integer");
}
If you run the main again, the output is still Number.
Basically, because it was decided at compile time. If you recompile the MethodChooser class (the one with the main), and run the program again, the output will be Integer.
As such, if you want to force the selection of one of the overloaded methods, the type of the arguments must correspond with the type of the parameters at compile time, and not only at run time as you seem to expect in this exercise.

Method/Constructor Overloading with Super/Sub types

I have some questions as to which overloaded method would be called in certain cases.
Case 1:
public void someMethod(Object obj){
System.out.println("Object");
}
public void someMethod(InputStream is){
System.out.println("InputStream");
}
public void someMethod(FilterInputStream fis){
System.out.println("FilterInputStream");
}
I know that if I pass it a String it will print "Object". However, what if I pass it an InputStream? It gets more confusing if I pass it something such as BufferedInputStream. Will this call the Object one, the InputStream one, or the FilterInputStream one? Does the order that the methods appear matter?
Case 2:
This is a little more tricky, because it takes advantage of multiple interface inheritance. Neither BlockingQueue and Deque are sub/supertypes of each other, but both are supertypes of BlockingDeque. Sun added multiple inheritance with interfaces because they don't need a tree structure. The declaration for BlockingDeque is
public interface BlockingDeque extends BlockingQueue, Deque {.
public void someMethod(BlockingQueue bq){
System.out.println("BlockingQueue");
}
public void someMethod(Deque bq){
System.out.println("Deque");
}
public void someCaller(){
BlockingDeque bd = new LinkedBlockingDeque();
someMethod(bd);
}
Will this Call someMethod(BlockingQueue) or someMethod(Deque)?
Case 3:
You can combine these two with this:
public void someMethod(Queue q){
//...
}
public void someMethod(Deque q){
//...
}
public void someMethod(List p){
//...
}
public void someCaller(){
someMethod(new LinkedList());
}
Same question: someMethod(Queue), someMethod(Deque), or someMethod(List)?
Case 4:
You can make things very complicated too, by introducting two arguments:
public void someMethod(Collection c1, List c2){
//...
}
public void someMethod(List c1, Collection c2){
//...
}
public void someCaller(){
someMethod(new ArrayList(), new ArrayList());
}
Will this call someMethod(Collection, List) or vice versa?
Case 5:
It gets worse when they have different return types:
public Class<?> someMethod(BlockingQueue bq){
return BlockingQueue.class;
}
public String someMethod(Deque bq){
return "Deque";
}
public void someCaller(){
BlockingDeque bd = new LinkedBlockingDeque();
System.out.println(someMethod(bd));
}
These can get pretty bad. What will someCaller print in this case? someMethod(BlockingQueue).toString(), or someMethod(Deque)?
In general, Java will invoke the narrowest non-ambiguous definition, so for the first few cases if you pass a narrow type it will invoke the narrowest function, if you pass a wider type (say InputStream) you get the wider type's function (in case 1 for InputStream that's method 2). Here's a simple test, and note that downcasting will widen the type, and call the wider type's method.
The core issue is whether Java can resolve a unique function for calling. So that means if you provide a definition that has multiple matches, you need to either match the highest known type, or uniquely match a wider type without also matching the higher type. Basically: if you match multiple functions, one of them needs to be higher in hierarchy for Java to resolve the difference, otherwise the calling convention is definitively ambiguous.
Java seems to throw a compilation error when the method signatures are ambiguous. In my view Case 4 is canonically the worst example of this, so I wrote a quick test and did in fact get the expected compilation error, complaining of an ambiguous match for functions to invoke.
Case 5 doesn't make anything better or worse: Java doesn't use return type to disambiguate which method to call, so it won't help you -- and since the definitions are already ambiguous you're still going to end up with a compilation error.
So the quick summary:
Compilation error due to ambiguous call when invoked with a plain InputStream, called with FilteredInputStream uses 3rd def, called with something that implements InputStream but isn't a FilteredInputStream uses 2nd def, anything else, 1st def
2nd def
ambiguous, will cause a compilation error
ambiguous, will cause a compilation error
ambiguous, will cause a compilation error
Finally, if you have doubts that you're calling the definition you think you should be, you should consider changing your code to remove the ambiguity or work to specify the right type argument(s) to call the "right" function. Java will tell you when it can't make a smart decision (when things are truly ambiguous), but the best way to avoid any of these problems is through consistent and unambiguous implementations. Don't do weird stuff, like case 4, and you won't run into weird problems.
In the case of overloaded functions, the method called will be the one which has the most restricted but compatible argument type in reference to the object being passed. Also something to note is that binding of overloaded method is decided at the compile time and not by the type of object determined at the runtime.e.g.
Case 1: If input is of type InputStream at compile time, then 2nd method will be called. BufferedInputStream will go into 2nd method.
Case 2: This fails at compile time because BlockingDeque type of reference is ambiguous and the argument could fit in any of the two methods as it extends both these types
case 3: No problem here, 3rd method because Linkedlist is not compatible with any of the two other arguments
Case 4: Ambiguous because with these arguments I can get into any of those two methods and there is no way to discern
Case 5: Return types have no role to play in overloaded methods. Case 2 holds.
This is a bit tangential to the question about the overloaded arguments, but there is a pretty crisp reason why case 5 is "worse".
Case 5 is where you're using the language feature called co-variant return types. This wasn't originally present in Java but was added in v1.5 I believe (partially because of this problem). If the compiler cannot figure out what the proper return type is it was fail and that is what happens in this case.

Categories