Java Institute DreamsCity 2000
Welcome to DreamsCity
Return to Java Institute

OVERLOAD RESOLUTION

Suppose that you're doing some Java programming, and you have code
like this:
public class OverDemo1 { static void f(float f) {} static void f(double d) {} public static void main(String args[]) { f(37); } } What happens here? It's possible to convert an integer value (37) to either a float or a double, so which of the f methods is called? Or is this program invalid? (The equivalent C++ program gives a compile error.) The answers to these questions relate to "overload resolution," that is, the rules that are applied to determine which method is called when there is a set of identically-named methods. The first rule says that a method is "applicable" in a given overloading case if the number of parameters in the method declaration matches the number of arguments in the method invocation, and the type of each argument can be converted to the type of the corresponding method parameter. You might think that a rule stating that the number of parameters and arguments must match would be obvious, but not necessarily so. In C++, functions are allowed default parameters, so that you can declare a function by saying: void f(int, double = 12.34); and then call it with: f(37); which is converted by the C++ compiler to: f(37, 12.34); The rule about conversions can be illustrated by a slight variation on the first example: public class OverDemo2 { static void f(float f) {} static void f(String s) {} public static void main(String args[]) { f(37); } } In this example, 37 can be converted to a float through a "widening primitive conversion." But there's no corresponding way to convert 37 to a String, so there's no issue of choosing one f method over the other. The compiler chooses f(float). The second rule is that a method declaration must be "accessible," that is, available at the point where the method is invoked. Here is another example: class A { public static void f(float f, double d) {} private static void f(double d, float f) {} } public class OverDemo3 { public static void main(String args[]) { A.f(37, 47); } } At the point where A.f is invoked within main, only one of the f methods from A is accessible; the other one is private and not available outside of the A class. This particular example will trigger an ambiguity error if both f methods are public. Note that it's never an error to simply declare more than one method with the same name but different parameter types. The error occurs at the point of method invocation if the compiler cannot determine which method to invoke. The third and final rule is the trickiest. Suppose that after the first two rules above are applied, there are still two or more methods with the same name that could possibly be called. For example, in the OverDemo1 program above, neither of the two rules already described removes either of the f methods from consideration. Both methods have the right number of parameters, it's possible to convert 37 to a float or a double, and both methods are accessible. So the third rule is to choose the most "specific" method. The rule is: if any method still under consideration has parameter types that are assignable to another method that's also still in play, then the other method is removed from consideration. This process is repeated until no other method can be eliminated. If the result is a single "most specific" method, then that method is called. If there's more than one method left, the call is ambiguous. Suppose that you have the methods: f(float) f(double) In this case, the parameter types for the first method are assignable to the parameter types of the second method, that is, it's legal to say: double = float through a widening primitive conversion. By contrast, saying: float = double is not valid without a cast. Based on this third rule, f(double) is removed from the set of possible methods to call, and therefore f(float) is called. You can confirm this behavior with another demo program: public class OverDemo4 { static void f(float f) {System.out.println("float");} static void f(double d) {System.out.println("double");} public static void main(String args[]) { f(37); } } which prints the value "float" when you run it. Suppose that you have another case, with two methods: f(float, double) f(double, float) and a call: f(37, 47) Here, the first parameter type of the first method (float) can be assigned to the first parameter type of the second method (double). But this doesn't work for the second parameter; you can't assign double to float. If you start with the second parameter and go in reverse order, you find the opposite to be true. That is, the first parameter type of the second method (double) cannot be assigned to the first parameter type of the first method (float). But you can assign the second parameter float to double. So neither of the f methods can be removed from consideration. Therefore the f(37, 47) call is ambiguous. In section 15.11.2.2 of the Java Language Specification there's a short paragraph that helps explain this rule: The informal intuition is that one method declaration 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 error. Another example will help to clarify this rule: class A {} class B extends A {} class C extends B {} public class OverDemo5 { static void f(A a) { System.out.println("f(A)"); } static void f(B b) { System.out.println("f(B)"); } public static void main(String args[]) { C cref = new C(); f(cref); } } In this example, f(B) is called, because it is more specific than f(A). Any invocation of f(B) could also be handled by f(A), but the reverse is not true. One final point about overload resolution: it's usually a good idea to avoid being clever with this mechanism, even if you understand it thoroughly. If you have a case where the parameter types are completely distinct, as in: void f(int) void f(String) this usage is good, but a case like: void f(float) void f(double) called with f(37) starts to get tricky and confusing.

Any comments? email to:
richard@dreamscity.net