views:

59

answers:

3

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)?

+3  A: 

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:

  1. 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

  2. 2nd def

  3. ambiguous, will cause a compilation error

  4. ambiguous, will cause a compilation error

  5. 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.

Mark E
Case 1: in case you do InputStream i = new FileInputStream(new File("new.txt")). Calling someMethod(i) will invoke 2nd method.. right?
Gaurav Saxena
@Guarav, that's what I said, albeit you're saying it more concisely.
Mark E
A: 

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.

sonnyrao
+1  A: 

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.

Gaurav Saxena
Look at the Javadoc for LinkedList: http://download-llnw.oracle.com/javase/6/docs/api/java/util/LinkedList.html
Leo Izen