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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With