How does the compiler choose between overloaded methods, and what makes two overloads ambiguous?
Overloading means several methods share a name but differ in parameter list. The compiler chooses between them using the types of the arguments at the call site, applying a fixed order: an exact match wins first, then widening primitive conversions, then boxing and unboxing, and finally a widening reference conversion. The most specific applicable method is chosen.
public class Main {
public static void main(String[] args) {
Printer p = new Printer();
p.print("hello");
p.print(42);
p.print(3.5, 2);
}
}
class Printer {
void print(String text) { System.out.println("text: " + text); }
void print(int number) { System.out.println("int: " + number); }
void print(double number, int times) {
for (int i = 0; i < times; i++) {
System.out.println("double: " + number);
}
}
}The output shows three distinct resolutions. The string argument picks the single-parameter string version, the integer picks the int version, and the two-argument call picks the version that takes a double and an int. Notice that the int argument is widened to a double in the last case, because no overload accepts a double and an int exactly, but one does accept a double and an int after widening.
Two overloads are ambiguous when neither is more specific than the other for the arguments supplied, such as a method taking a long and one taking a double called with an int literal. The compiler reports the ambiguity rather than picking one, which is the right behaviour.
The risk to watch for is that adding an overload can change how existing code compiles. If a call was previously resolved to one method and a new overload is a better match, the call silently switches to the new method. That is why some teams restrict overloading to a small, coherent family rather than scattering it across a class.