3.6 Method Overloading, Overload Resolution, and Varargs

Key Takeaways

  • A method signature is the name plus the number, types, and order of parameters; return type, throws clause, parameter names, and modifiers are excluded, so two methods differing only in return type do not compile.
  • Overload resolution runs three passes and stops at the first that finds a candidate: strict (widening primitive only), loose (adds boxing/unboxing), then variable arity, so widening beats boxing and boxing beats varargs.
  • A method invocation conversion may widen a primitive or box it but never widen then box, so passing an int to a Long parameter fails while passing it to an Object parameter succeeds.
  • A var-arg parameter compiles to an array, must be last, and may appear at most once; f(int...) and f(int[]) therefore clash as duplicate signatures.
  • Omitting all var-arg arguments passes a zero-length array, but passing a bare null passes null as the entire array, so reading its length throws NullPointerException.
Last updated: September 2026

Method Overloading, Overload Resolution, and Varargs

Oracle lists "Implement overloaded methods, including var-arg methods" as its own sub-objective under Using Object-Oriented Concepts in Java, and the 1Z0-830 exam mines it relentlessly. Overloading questions rarely ask "is this legal?" — they show four candidate methods, one call, and ask which one runs. Getting that right means knowing the compiler's resolution algorithm, not guessing.


1. What Actually Distinguishes Two Overloads

A method signature consists of the method name plus the number, types, and order of its formal parameters. Nothing else counts:

ElementPart of the signature?
Method nameYes
Parameter types and their orderYes
Parameter namesNo
Return typeNo
throws clauseNo
Access modifier (public, private)No
static / final / synchronizedNo
int  parse(String s)             { return 0; }
long parse(String s)             { return 0L; }   // ERROR: same signature, only return type differs
void log(String msg)             { }
void log(String message)         { }              // ERROR: parameter names are irrelevant
void log(String msg) throws IOException { }       // ERROR: throws is not part of the signature

Legal overloads must differ in arity or in at least one parameter type:

void log(String msg) { }
void log(String msg, int level) { }   // OK: different arity
void log(StringBuilder msg) { }       // OK: different parameter type

[!CAUTION] Generic type arguments are erased, so handle(List<String>) and handle(List<Integer>) collide (see section 6.2). Erasure runs before the duplicate-signature check.


2. The Three-Phase Resolution Algorithm

When several overloads share a name, the compiler runs up to three passes and stops at the first pass that finds at least one applicable method. This ordering is the single highest-yield fact in this section.

PhaseConversions allowedVarargs considered?
1 — StrictIdentity, subtyping, widening primitive (intlongfloatdouble)No
2 — LooseEverything in phase 1, plus boxing/unboxing, and boxing followed by a widening reference conversionNo
3 — Variable arityEverything in phase 2, plus varargs expansionYes

The practical slogan: widening beats boxing, and boxing beats varargs.

static void f(long x)    { System.out.print("long ");    }
static void f(Integer x) { System.out.print("Integer "); }
static void f(Object x)  { System.out.print("Object ");  }
static void f(int... x)  { System.out.print("varargs "); }

f(5);   // prints "long"

Now delete the overloads one at a time and re-run f(5):

Remaining overloadsWinnerWhy
long, Integer, Object, int...longPhase 1 succeeds via widening intlong
Integer, Object, int...IntegerPhase 2; Integer is more specific than Object
Object, int...ObjectPhase 2 via boxing then widening reference conversion
int... onlyint...Only phase 3 has an applicable method

The Conversion That Does Not Exist

A method invocation conversion may widen a primitive or box it, but never both in that order:

static void g(Long x) { }
g(5);      // ERROR: int -> long -> Long is not a method invocation conversion
g(5L);     // OK:    long -> Long is a plain boxing conversion
g((long) 5); // OK

The reverse direction is legal, because boxing may be followed by a widening reference conversion: intIntegerNumberObject.


3. Choosing Between Reference Overloads

Within a single phase, the compiler picks the most specific applicable method — the one whose parameter type can be passed to all the others.

static void h(Object o) { System.out.print("Object"); }
static void h(String s) { System.out.print("String"); }

h(null);   // prints "String" — String is more specific than Object

If two candidates are unrelated, neither is more specific and the call is ambiguous:

static void k(String s)        { }
static void k(StringBuilder b) { }

k(null);   // ERROR: reference to k is ambiguous
k((String) null);   // OK — the cast picks the overload

Overloading Is Resolved at Compile Time

This is the trap that separates overloading from overriding. Overriding dispatches on the runtime object; overloading is decided from the declared type of the argument expression:

Object value = "hello";     // declared Object, runtime String
h(value);                   // prints "Object", NOT "String"

4. Var-Arg Methods

A variable-arity (var-arg) parameter is written Type... name and is compiled to Type[].

Declaration rules

  • A method may declare at most one var-arg parameter.
  • It must be the last parameter: void report(String label, int... values) is legal; void report(int... values, String label) is not.
  • void f(int... a) and void f(int[] a) cannot coexist — after desugaring they have the same signature, and the compiler rejects the class.

Call-site behaviour

static int total(String label, int... values) { return values.length; }

total("a");                       // 0  -> an EMPTY array is created, never null
total("a", 1, 2, 3);              // 3
total("a", new int[]{1, 2, 3});   // 3  -> an existing array may be passed directly

[!IMPORTANT] Passing a bare null to a var-arg parameter passes it as the whole array, not as one element. values is then null, and values.length throws NullPointerException. Write total("a", (int) 0) for a single element, or cast — printAll((Object) null) — when the parameter is Object....

Ambiguity between var-arg overloads is resolved by the same most-specific rule, applied to the element types:

static void show(Object... args) { System.out.print("Object..."); }
static void show(String... args) { System.out.print("String..."); }

show("a", "b");   // prints "String..." — String[] is a subtype of Object[]
show(1, "b");     // prints "Object..." — only this one is applicable

Because generic var-args create an unchecked heap-pollution warning, mark provably safe generic var-arg methods with @SafeVarargs (section 5.5) — it is permitted only on static, final, or private instance methods and on constructors.


5. Exam Traps Checklist

  1. Autoboxing never outranks widening. f(5) with f(long) and f(Integer) present picks long.
  2. Varargs is always last resort, even when it looks like an exact match.
  3. intLong fails; intObject succeeds.
  4. Null with two unrelated reference overloads is ambiguous, not "picks the first one".
  5. A missing var-arg argument yields a zero-length array, so values.length is 0, never an exception.
  6. f(int...) and f(int[]) clash; f(int...) and f(int, int) do not.
  7. Return type differences never create an overload.
Loading diagram...
Three-Phase Overload Resolution and Its Failure Modes
Test Your Knowledge

Given the following overloads, what does the call print?

static void f(long x)    { System.out.print("long"); }
static void f(Integer x) { System.out.print("Integer"); }
static void f(Object x)  { System.out.print("Object"); }
static void f(int... x)  { System.out.print("varargs"); }

public static void main(String[] args) {
    f(5);
}

A
B
C
D
Test Your Knowledge

What is the compilation result of the following class?

public class Reporter {
    void send(int[] data)  { System.out.print("array"); }
    void send(int... data) { System.out.print("varargs"); }
}

A
B
C
D
Test Your Knowledge

What is the result of running this program?

public class VarargsNull {
    static int count(Object... items) {
        return items.length;
    }
    public static void main(String[] args) {
        System.out.println(count(null));
    }
}

A
B
C
D
Test Your Knowledge

Which pair of declarations inside a single class is NOT a legal overload?

A
B
C
D