00:00

Method Overloading in Java – Rules and Examples

Method overloading means defining several methods in the same class with the same name but different parameter lists. The compiler decides which one to call by looking at the number, types and order of the arguments. Overloading is Java's form of compile-time polymorphism.

You use overloading constantly: System.out.println has overloads for int, double, String, Object and more. This tutorial covers the valid ways to overload, the exact resolution rules the compiler follows, ambiguous calls, and overloaded constructors.

1. Ways to Overload a Method

Overloaded methods must differ in their parameter list:

OverloadingWays.java
public class OverloadingWays {
    // 1. Different number of parameters
    static int sum(int a, int b)              { return a + b; }
    static int sum(int a, int b, int c)       { return a + b + c; }

    // 2. Different parameter types
    static double sum(double a, double b)     { return a + b; }
    static String sum(String a, String b)     { return a + b; }

    // 3. Different order of parameter types
    static String tag(String name, int id)    { return name + "#" + id; }
    static String tag(int id, String name)    { return id + ":" + name; }

    public static void main(String[] args) {
        System.out.println(sum(2, 3));
        System.out.println(sum(2, 3, 4));
        System.out.println(sum(2.5, 1.25));
        System.out.println(sum("Java", "ByTechie"));
        System.out.println(tag("order", 42));
        System.out.println(tag(42, "order"));
    }
}
Output
5
9
3.75
JavaByTechie
order#42
42:order
ChangeValid overload?
Number of parametersYes
Types of parametersYes
Order of parameter typesYes (but use with care: it is easy to confuse)
Only the return typeNo: compile error, "method is already defined"
Only parameter namesNo
Only access modifier, static or throws clauseNo
Generic types that erase to the same type, such as List<String> vs List<Integer>No: "name clash: same erasure"

2. How the Compiler Chooses an Overload

When no overload matches the argument types exactly, the compiler searches in three phases and stops at the first phase that finds a match:

  1. Exact match or widening of primitives (byte → short → int → long → float → double) and subtype to supertype references. No boxing, no varargs.
  2. Boxing and unboxing allowed (int ↔ Integer).
  3. Varargs allowed.

Within a phase, the most specific method wins.

OverloadResolution.java
public class OverloadResolution {
    static void show(int x)     { System.out.println("int:     " + x); }
    static void show(long x)    { System.out.println("long:    " + x); }
    static void show(double x)  { System.out.println("double:  " + x); }
    static void show(Integer x) { System.out.println("Integer: " + x); }
    static void show(Object x)  { System.out.println("Object:  " + x); }
    static void show(int... x)  { System.out.println("varargs: " + x.length + " values"); }

    public static void main(String[] args) {
        byte b = 10;
        show(b);                  // phase 1: widening byte -> int (exact type not available)
        show(5);                  // exact match: int
        show(5L);                 // exact match: long
        show(5.0f);               // widening float -> double
        show(Integer.valueOf(7)); // exact match: Integer
        show("text");             // String -> Object
        show(1, 2, 3);            // only varargs fits
        show();                   // varargs with zero arguments
    }
}
Output
int:     10
int:     5
long:    5
double:  5.0
Integer: 7
Object:  text
varargs: 3 values
varargs: 0 values

Note how show(b) with a byte chose int (widening, phase 1) over Integer or Object (boxing, phase 2), and varargs was used only when nothing else fit.

3. Ambiguous Calls

If two overloads are equally specific, the call is ambiguous and does not compile. Passing null picks the most specific reference type:

AmbiguousCall.java
public class AmbiguousCall {
    static void print(Integer x, long y) { System.out.println("Integer, long"); }
    static void print(long x, Integer y) { System.out.println("long, Integer"); }

    static void find(String s)  { System.out.println("String version"); }
    static void find(Object o)  { System.out.println("Object version"); }

    public static void main(String[] args) {
        // print(1, 2);  // compile error: reference to print is ambiguous
        print(Integer.valueOf(1), 2L);   // explicit types remove the ambiguity
        find(null);                      // String is more specific than Object -> String version
        find((Object) null);             // cast chooses the Object version
    }
}
Output
Integer, long
String version
Object version

Fix ambiguity with explicit casts or argument types, or, better, by designing overloads that cannot be confused.

4. Constructor Overloading

Constructors are overloaded the same way, giving callers several ways to create an object. Chaining them with this(...) keeps the logic in one place:

OverloadedConstructors.java
public class OverloadedConstructors {
    static class Pizza {
        private final String size;
        private final String crust;
        private final int toppings;

        Pizza()                                  { this("Medium"); }
        Pizza(String size)                       { this(size, "Classic"); }
        Pizza(String size, String crust)         { this(size, crust, 1); }
        Pizza(String size, String crust, int t)  { this.size = size; this.crust = crust; this.toppings = t; }

        public String toString() { return size + " " + crust + " pizza with " + toppings + " topping(s)"; }
    }

    public static void main(String[] args) {
        System.out.println(new Pizza());
        System.out.println(new Pizza("Large"));
        System.out.println(new Pizza("Small", "Thin"));
        System.out.println(new Pizza("Large", "Cheese Burst", 4));
    }
}
Output
Medium Classic pizza with 1 topping(s)
Large Classic pizza with 1 topping(s)
Small Thin pizza with 1 topping(s)
Large Cheese Burst pizza with 4 topping(s)

See Constructors in Java and Constructor Chaining.

5. Other Overloading Facts

  • main can be overloaded, but the JVM only starts the program with public static void main(String[] args).
  • Static methods can be overloaded, and a static and an instance method can overload each other if their parameter lists differ.
  • Overloading across inheritance: a subclass can add an overload of a method inherited from its parent.
  • Overloading is resolved at compile time using the declared types of the arguments, not their runtime types.

6. Best Practices

  • Overloads should do the same thing with different inputs; different behaviour deserves a different name.
  • Avoid overloads with the same number of parameters where the types are related by boxing or inheritance (remove(int) vs remove(Object) in List is a famous trap).
  • Prefer distinct names (ofSeconds, ofMinutes) or builders when many combinations exist.
  • Delegate from simpler overloads to the most complete one.

7. Summary

  • Overloading means same method name, different parameter lists, in the same class or hierarchy.
  • Return type, parameter names and modifiers alone cannot distinguish overloads.
  • The compiler chooses in phases: exact/widening, then boxing, then varargs; the most specific wins.
  • Ambiguous calls fail to compile; constructors are overloaded the same way.

Related Tutorials