🧩 Methods · Beginner

Method overloading in Java

Same name, different parameter lists; resolution order (exact, widening, boxing, varargs).

🧩 The mysteryprintln(5), println("hi"), println(true): one name, three different jobs. How does Java know which println you meant?

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) wins
🔮 Predict it

Most 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));
}
  1. long double
  2. double double
  3. long long
  4. Compile 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.

🔮 Predict it

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);
}
  1. Integer
  2. double
  3. Compile 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.

🔮 Predict it

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);
}
  1. varargs
  2. boxed
  3. Compile 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.

⚠️ The trap

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: ambiguous
💼 In the real world

In 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

  1. Overloads must differ in parameter types, count or order
  2. Return type alone can't distinguish overloads
  3. Order: widening beats boxing, and boxing beats varargs
  4. If no single best match exists, the call is ambiguous: compile error
🤯 Did you know?

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'));
}
  1. int double int
  2. int double double
  3. int double char
  4. 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);
}
  1. Integer
  2. long
  3. 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.

Next: how can printf accept 1, 2 or 20 arguments? Meet the three magic dots.