Skip to content
Java polymorphism 6 min read

Method Overloading

Method overloading lets you use the same method name for different variations of a task, each accepting different parameters. The Java compiler inspects the argument list at each call site and picks the correct version automatically — no runtime cost, no guessing.

What Is Method Overloading?

When a class contains two or more methods that share the same name but have different parameter lists, that is method overloading. It is the primary mechanism behind compile-time polymorphism.

class Printer {
    void print(String message) {
        System.out.println("String: " + message);
    }

    void print(int number) {
        System.out.println("int: " + number);
    }

    void print(double value) {
        System.out.println("double: " + value);
    }
}

public class Main {
    public static void main(String[] args) {
        Printer p = new Printer();
        p.print("Hello");   // String: Hello
        p.print(42);        // int: 42
        p.print(3.14);      // double: 3.14
    }
}

Output:

String: Hello
int: 42
double: 3.14

The compiler resolves each call entirely at compile time by matching the argument types to the available signatures.

Three Ways to Overload a Method

You can differentiate overloaded methods by changing any of the following:

StrategyExample signatures
Number of parametersadd(int a) vs add(int a, int b)
Type of parametersadd(int a, int b) vs add(double a, double b)
Order of parameter typesshow(int a, String b) vs show(String a, int b)

1. Different Number of Parameters

class Area {
    // square
    double calculate(double side) {
        return side * side;
    }

    // rectangle
    double calculate(double length, double width) {
        return length * width;
    }

    // cuboid surface area
    double calculate(double l, double w, double h) {
        return 2 * (l * w + w * h + l * h);
    }
}

2. Different Parameter Types

class Converter {
    String stringify(int value) {
        return "int:" + value;
    }

    String stringify(double value) {
        return "double:" + value;
    }

    String stringify(boolean value) {
        return "bool:" + value;
    }
}

3. Different Order of Parameter Types

class Info {
    void display(String name, int age) {
        System.out.println(name + " is " + age + " years old.");
    }

    void display(int age, String name) {
        System.out.println("Age " + age + " — " + name);
    }
}

Tip: Changing only the order of parameters to create an overload works, but avoid it unless the meaning genuinely differs — it makes code harder to read and easy to call by accident.

What Does NOT Count as Overloading

Return Type Alone Is Not Enough

// This does NOT compile — same name, same parameters, different return type only
class Bad {
    int getValue() { return 1; }
    // double getValue() { return 1.0; } // ❌ compile error: duplicate method
}

The compiler cannot distinguish two methods purely by return type because a caller can ignore the return value entirely.

Access Modifiers and Exceptions Do Not Count

Changing public to private, or adding a throws clause, does not create an overload — the parameter list must actually differ.

Warning: Attempting to overload by return type alone causes a compile-time error: method getValue() is already defined.

Type Promotion in Overload Resolution

When no exact match exists, Java automatically widens (promotes) the argument to the next compatible type. The promotion chain is:

byte → short → int → long → float → double
char → int → long → float → double
class Demo {
    void show(long x) {
        System.out.println("long: " + x);
    }

    void show(double x) {
        System.out.println("double: " + x);
    }
}

public class Main {
    public static void main(String[] args) {
        Demo d = new Demo();
        d.show(10);    // int promoted to long  → "long: 10"
        d.show(10.5f); // float promoted to double → "double: 10.5"
    }
}

Output:

long: 10
double: 10.5

Note: Java picks the most specific matching type. If both int and long versions exist when you pass an int, the exact int version wins.

Overloading with Varargs

You can overload using varargs, but be careful — varargs are the last resort in resolution and can cause ambiguity.

class Logger {
    void log(String msg) {
        System.out.println("Fixed: " + msg);
    }

    void log(String... msgs) {
        System.out.println("Varargs: " + msgs.length + " messages");
    }
}

public class Main {
    public static void main(String[] args) {
        Logger l = new Logger();
        l.log("hello");         // Fixed: hello  (exact match wins)
        l.log("a", "b", "c");  // Varargs: 3 messages
    }
}

Warning: Calling l.log() (zero arguments) against a varargs overload can be ambiguous if another zero-arg version also exists. Prefer naming such methods differently.

Overloading Constructors

Constructors follow the same rules — you can overload them to offer flexible ways to create an object.

class Point {
    double x, y, z;

    Point() {
        this(0, 0, 0);
    }

    Point(double x, double y) {
        this(x, y, 0);
    }

    Point(double x, double y, double z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    @Override
    public String toString() {
        return "(" + x + ", " + y + ", " + z + ")";
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println(new Point());          // (0.0, 0.0, 0.0)
        System.out.println(new Point(1, 2));      // (1.0, 2.0, 0.0)
        System.out.println(new Point(1, 2, 3));   // (1.0, 2.0, 3.0)
    }
}

Output:

(0.0, 0.0, 0.0)
(1.0, 2.0, 0.0)
(1.0, 2.0, 3.0)

The this(...) delegation pattern keeps every constructor DRY — only the most-parameterized version contains actual logic.

Overloading vs Overriding at a Glance

It is easy to mix up the two. Here is the key distinction:

FeatureOverloadingOverriding
WhereSame classSubclass
Resolved byCompilerJVM at runtime
SignatureMust differMust be identical
Return typeCan differ freelyMust be same or covariant
@Override annotationNot applicableRecommended
Polymorphism typeStatic (compile-time)Dynamic (runtime)

See Method Overriding and Overloading vs Overriding for a deeper comparison.

Under the Hood

At the bytecode level, each overloaded method is stored as a completely separate entry in the class’s constant pool with its own descriptor (name + parameter types encoded as a signature string, e.g. (ILjava/lang/String;)V). There is no if/switch at runtime — the correct method reference is baked directly into the call-site bytecode (invokevirtual or invokestatic) during compilation.

You can inspect this with the javap -c tool (see javap Tool):

// javap -c Area.class (abbreviated)
double calculate(double);
  descriptor: (D)D

double calculate(double, double);
  descriptor: (DD)D

double calculate(double, double, double);
  descriptor: (DDD)D

Each descriptor is unique, so the JVM never needs to disambiguate — the compiler already did that work. This is why overloading has zero runtime overhead compared to a single method with conditional logic.

The JIT compiler also benefits: because each overloaded variant is a separate method, the JIT can inline and optimize each call path independently based on the actual argument types it observes at runtime.

Tip: If you need polymorphic behavior that depends on the runtime type of an object (not just the parameter types), you want runtime polymorphism via overriding — the compiler cannot resolve that at compile time.

Common Pitfalls

  • Autoboxing ambiguity — passing an int when both Integer and long overloads exist can surprise you. Java prefers widening over autoboxing, so long wins over Integer.
  • Null argumentsnull is assignable to any reference type, so foo(null) is ambiguous if multiple reference-type overloads exist. Cast explicitly: foo((String) null).
  • Overloading across inheritance — a method in a subclass with the same name but different parameter types overloads, not overrides, the parent’s method. Use @Override to catch mistakes.
Last updated June 13, 2026
Was this helpful?