W3docs

Java Method Overloading

Define multiple methods with the same name but different parameter lists in Java to provide flexible APIs.

Method overloading is declaring two or more methods in the same class with the same name but different parameter lists. The compiler picks which one to call based on the argument types you pass.

You've already used overloaded methods without realizing it. System.out.println(...) has versions that take an int, a double, a String, an Object, a char[], and so on. They all behave similarly — print the value and a newline — but the implementation differs by type.

What "different parameter list" means

Two overloads must differ in arity (number of parameters) or the types of those parameters in order. They may not differ only in:

  • Parameter names
  • Return type
  • Whether parameters are final
public static int square(int n) { return n * n; }

// VALID overloads — different parameter types
public static double square(double n) { return n * n; }
public static long   square(long   n) { return n * n; }

// VALID overload — different arity
public static int sum(int a, int b)             { return a + b; }
public static int sum(int a, int b, int c)      { return a + b + c; }

// INVALID — only the return type differs
// public static long square(int n) { return (long) n * n; }   // won't compile

How Java picks an overload

When you write square(3), the compiler does this in order:

  1. Exact match. Is there an overload whose parameters match the argument types exactly? square(3)square(int). Done.
  2. Widening. If not, can the arguments be widened (e.g., int → long, int → double)? Pick the overload that requires the smallest widening.
  3. Autoboxing / unboxing. Otherwise, try wrapping or unwrapping (int ↔ Integer).
  4. Varargs. As a last resort, fall back to a varargs overload (see the varargs chapter).

If two overloads tie at the same step, the call is ambiguous and won't compile.

public static void show(int n)    { System.out.println("int: " + n); }
public static void show(long n)   { System.out.println("long: " + n); }
public static void show(double n) { System.out.println("double: " + n); }

show(3);     // exact match → int
show(3L);    // exact match → long
show(3.0);   // exact match → double
show((short) 3);   // widens short → int (closest), picks show(int)

Ambiguity

When the compiler can't decide between two equally good overloads, you get an error:

public static void f(int a, long b)  { /* ... */ }
public static void f(long a, int b)  { /* ... */ }

f(1, 2);   // ERROR: reference to f is ambiguous

Both overloads require widening one argument from int to long. Neither is "better." The fix is to disambiguate at the call site with an explicit cast — f(1, 2L) or f(1L, 2) — or to add a third overload f(int, int) that handles the case exactly.

Overloading on object types

Reference types overload the same way, but with subtype relationships instead of widening:

public static void log(Object o) { System.out.println("Object: " + o); }
public static void log(String s) { System.out.println("String: " + s); }

log("hello");        // exact match → String
log(42);             // autobox to Integer, then Integer is-a Object → log(Object)
log((Object) "hi");  // forces the Object overload

If you pass a String and the only overload takes Object, Java is happy — String is an Object. But if there's a more specific overload, Java prefers it.

When overloading is helpful

The point of overloading is to give callers a clean, type-natural API. Two patterns come up most often:

Default values. A short overload calls the long one with sensible defaults:

public static void greet(String name) {
  greet(name, 1);                 // delegate
}

public static void greet(String name, int times) {
  for (int i = 0; i < times; i++) {
    System.out.println("Hello, " + name);
  }
}

Convenience converters. Different input types, same logical operation:

public static int lengthOf(String s) { return s == null ? 0 : s.length(); }
public static int lengthOf(int[] xs) { return xs == null ? 0 : xs.length; }
public static int lengthOf(int n)    { return Integer.toString(n).length(); }

The caller writes lengthOf(x) without thinking; the compiler routes to the right body.

When not to overload

If two overloads would do substantially different things, give them different names instead. The reader of format(x, y) shouldn't need to look up which overload was picked to know what the call means. Overloads should be variations of the same idea, not different ideas sharing a name.

A worked example

java— editable, runs on the server

What's next

Overloading lets one name point at several methods. Sometimes you want a single method to call itself — solving a problem by chipping a piece off and asking a smaller version of the same problem to handle the rest. That's recursion.

Practice

Practice

Which difference between two methods is NOT enough to make them valid overloads?