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.
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:
| Element | Part of the signature? |
|---|---|
| Method name | Yes |
| Parameter types and their order | Yes |
| Parameter names | No |
| Return type | No |
throws clause | No |
Access modifier (public, private) | No |
static / final / synchronized | No |
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>)andhandle(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.
| Phase | Conversions allowed | Varargs considered? |
|---|---|---|
| 1 — Strict | Identity, subtyping, widening primitive (int → long → float → double) | No |
| 2 — Loose | Everything in phase 1, plus boxing/unboxing, and boxing followed by a widening reference conversion | No |
| 3 — Variable arity | Everything in phase 2, plus varargs expansion | Yes |
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 overloads | Winner | Why |
|---|---|---|
long, Integer, Object, int... | long | Phase 1 succeeds via widening int → long |
Integer, Object, int... | Integer | Phase 2; Integer is more specific than Object |
Object, int... | Object | Phase 2 via boxing then widening reference conversion |
int... only | int... | 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: int → Integer → Number → Object.
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)andvoid 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
nullto a var-arg parameter passes it as the whole array, not as one element.valuesis thennull, andvalues.lengththrowsNullPointerException. Writetotal("a", (int) 0)for a single element, or cast —printAll((Object) null)— when the parameter isObject....
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
- Autoboxing never outranks widening.
f(5)withf(long)andf(Integer)present pickslong. - Varargs is always last resort, even when it looks like an exact match.
int→Longfails;int→Objectsucceeds.- Null with two unrelated reference overloads is ambiguous, not "picks the first one".
- A missing var-arg argument yields a zero-length array, so
values.lengthis0, never an exception. f(int...)andf(int[])clash;f(int...)andf(int, int)do not.- Return type differences never create an overload.
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);
}
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"); }
}
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));
}
}
Which pair of declarations inside a single class is NOT a legal overload?