Method overloading in Java
Same name, different parameter lists; resolution order (exact, widening, boxing, varargs).
One name, many shapes
Overloading means several methods share a name but differ in their parameter lists: number, types or order. They can't differ by return type alone: a call like f(); doesn't say which return type it wants.
static void print(int x) { }
static void print(double x) { }
static void print(String s) { }
static int print(int x) { } // error!The compiler's three phases
The compiler picks an overload at compile time from the argument types, in phases. Phase 1: exact match or widening (int to long to double), no boxing. Phase 2: boxing allowed (int to Integer). Phase 3: varargs. The first phase that finds a match wins; within it, the most specific method wins.
// f(7) with these candidates:
f(long x) // phase 1: widening
f(Integer x) // phase 2: boxing
f(int... x) // phase 3: varargs
// -> f(long) winsMost specific wins
Neither method takes an int or a float exactly. What prints?
static String t(long x) { return "long"; }
static String t(double x) { return "double"; }
void main() {
System.out.println(t(5));
System.out.println(t(5f));
}long doubledouble doublelong longCompile error
Show the answer
An int can widen to long or double; long is more specific (a long fits in a double, not vice versa), so t(5) picks long. A float can't widen to long, only to double. Same idea: a char argument picks an int overload over a double one.
Widening vs boxing
What prints?
static void f(double x) {
System.out.println("double");
}
static void f(Integer x) {
System.out.println("Integer");
}
void main() {
f(7);
}IntegerdoubleCompile error
Show the answer
double. Phase 1 only allows widening, and int to double is widening, so f(double) is found right away. Boxing is never even considered.
Boxing vs varargs
What prints?
static void h(Integer x) {
System.out.println("boxed");
}
static void h(int... xs) {
System.out.println("varargs");
}
void main() {
h(3);
}varargsboxedCompile error
Show the answer
boxed. Phase 1 finds nothing (int to Integer needs boxing). Phase 2 allows boxing, so h(Integer) matches. Varargs only get a chance in phase 3, which is never reached.
When it's a tie
If two methods match in the same phase and neither is more specific, the compiler refuses to guess: reference to m is ambiguous. Below, each method needs exactly one widening for m(1, 2).
static void m(int a, long b) { }
static void m(long a, int b) { }
m(1, 2); // compile error: ambiguousIn real projects
Overload rules bite in real code: List<Integer> has remove(int index) and remove(Object o). list.remove(1) removes the element at index 1, not the value 1! To remove the value, write list.remove(Integer.valueOf(1)).
Key takeaways
- Overloads must differ in parameter types, count or order
- Return type alone can't distinguish overloads
- Order: widening beats boxing, and boxing beats varargs
- If no single best match exists, the call is ambiguous: compile error
System.out.println has 10 overloads: no arguments, boolean, char, int, long, float, double, char[], String and Object. Every println you've ever written picked one of them at compile time.
Practice questions
What does this print?
static String t(int x) { return "int"; }
static String t(double x) { return "double"; }
void main() {
System.out.println(t(3));
System.out.println(t(3.0));
System.out.println(t('a'));
}- int double int
- int double double
- int double char
- Compile error
Check your answer
int double int. 3 is an int and 3.0 is a double: exact matches. A char can widen to int or double; int is the more specific choice, so t('a') picks the int version.
What does this print?
static void f(long x) {
System.out.println("long");
}
static void f(Integer x) {
System.out.println("Integer");
}
void main() {
f(5);
}- Integer
- long
- Compile error
Check your answer
long. The compiler first looks for methods that work without boxing. int to long is a widening conversion, so f(long) is found in that first phase and boxing is never considered.