You want print() to work with strings, integers, and doubles. Instead of printString(), printInt(), printDouble(), overloading lets you use one name with multiple parameter types.

Overload for different types

Create methods with the same name but different parameter types.

AddOverload.java
public class AddOverload {
    public static void main(String[] args) {
        System.out.println("=== Method Overloading: add() ===\n");

        // Integer addition
        int left = ;
        int right = ;
        int intResult = add(left, right);
        System.out.println("add(" + left + ", " + right + ") = " + intResult);

        // Double addition
        double doubleResult = add(5.5, 3.2);
        System.out.println("add(5.5, 3.2) = " + doubleResult);

        // Three integers
        int tripleResult = add(10, 20, 30);
        System.out.println("add(10, 20, 30) = " + tripleResult);

        // More types
        long longResult = add(1000000000L, 2000000000L);
        System.out.println("add(1000000000L, 2000000000L) = " + longResult);

        // Compiler chooses the right method!
        System.out.println("\n=== Compiler Decision ===");
        System.out.println("add(1, 2)     → int version");
        System.out.println("add(1.0, 2.0) → double version");
        System.out.println("add(1, 2, 3)  → three-param version");
    }

    // Overload 1: Two integers
    static int add(int a, int b) {
        System.out.println("  [Called: add(int, int)]");
        return a + b;
    }

    // Overload 2: Two doubles
    static double add(double a, double b) {
        System.out.println("  [Called: add(double, double)]");
        return a + b;
    }

    // Overload 3: Three integers
    static int add(int a, int b, int c) {
        System.out.println("  [Called: add(int, int, int)]");
        return a + b + c;
    }

    // Overload 4: Two longs
    static long add(long a, long b) {
        System.out.println("  [Called: add(long, long)]");
        return a + b;
    }
}
public class AddOverload {
    public static void main(String[] args) {
        System.out.println("=== Method Overloading: add() ===\n");

        // Integer addition
        int left = ;
        int right = ;
        int intResult = add(left, right);
        System.out.println("add(" + left + ", " + right + ") = " + intResult);

        // Double addition
        double doubleResult = add(5.5, 3.2);
        System.out.println("add(5.5, 3.2) = " + doubleResult);

        // Three integers
        int tripleResult = add(10, 20, 30);
        System.out.println("add(10, 20, 30) = " + tripleResult);

        // More types
        long longResult = add(1000000000L, 2000000000L);
        System.out.println("add(1000000000L, 2000000000L) = " + longResult);

        // Compiler chooses the right method!
        System.out.println("\n=== Compiler Decision ===");
        System.out.println("add(1, 2)     → int version");
        System.out.println("add(1.0, 2.0) → double version");
        System.out.println("add(1, 2, 3)  → three-param version");
    }

    // Overload 1: Two integers
    static int add(int a, int b) {
        System.out.println("  [Called: add(int, int)]");
        return a + b;
    }

    // Overload 2: Two doubles
    static double add(double a, double b) {
        System.out.println("  [Called: add(double, double)]");
        return a + b;
    }

    // Overload 3: Three integers
    static int add(int a, int b, int c) {
        System.out.println("  [Called: add(int, int, int)]");
        return a + b + c;
    }

    // Overload 4: Two longs
    static long add(long a, long b) {
        System.out.println("  [Called: add(long, long)]");
        return a + b;
    }
}
public class AddOverload {
    public static void main(String[] args) {
        System.out.println("=== Method Overloading: add() ===\n");

        // Integer addition
        int left = ;
        int right = ;
        int intResult = add(left, right);
        System.out.println("add(" + left + ", " + right + ") = " + intResult);

        // Double addition
        double doubleResult = add(5.5, 3.2);
        System.out.println("add(5.5, 3.2) = " + doubleResult);

        // Three integers
        int tripleResult = add(10, 20, 30);
        System.out.println("add(10, 20, 30) = " + tripleResult);

        // More types
        long longResult = add(1000000000L, 2000000000L);
        System.out.println("add(1000000000L, 2000000000L) = " + longResult);

        // Compiler chooses the right method!
        System.out.println("\n=== Compiler Decision ===");
        System.out.println("add(1, 2)     → int version");
        System.out.println("add(1.0, 2.0) → double version");
        System.out.println("add(1, 2, 3)  → three-param version");
    }

    // Overload 1: Two integers
    static int add(int a, int b) {
        System.out.println("  [Called: add(int, int)]");
        return a + b;
    }

    // Overload 2: Two doubles
    static double add(double a, double b) {
        System.out.println("  [Called: add(double, double)]");
        return a + b;
    }

    // Overload 3: Three integers
    static int add(int a, int b, int c) {
        System.out.println("  [Called: add(int, int, int)]");
        return a + b + c;
    }

    // Overload 4: Two longs
    static long add(long a, long b) {
        System.out.println("  [Called: add(long, long)]");
        return a + b;
    }
}
public class AddOverload {
    public static void main(String[] args) {
        System.out.println("=== Method Overloading: add() ===\n");

        // Integer addition
        int left = ;
        int right = ;
        int intResult = add(left, right);
        System.out.println("add(" + left + ", " + right + ") = " + intResult);

        // Double addition
        double doubleResult = add(5.5, 3.2);
        System.out.println("add(5.5, 3.2) = " + doubleResult);

        // Three integers
        int tripleResult = add(10, 20, 30);
        System.out.println("add(10, 20, 30) = " + tripleResult);

        // More types
        long longResult = add(1000000000L, 2000000000L);
        System.out.println("add(1000000000L, 2000000000L) = " + longResult);

        // Compiler chooses the right method!
        System.out.println("\n=== Compiler Decision ===");
        System.out.println("add(1, 2)     → int version");
        System.out.println("add(1.0, 2.0) → double version");
        System.out.println("add(1, 2, 3)  → three-param version");
    }

    // Overload 1: Two integers
    static int add(int a, int b) {
        System.out.println("  [Called: add(int, int)]");
        return a + b;
    }

    // Overload 2: Two doubles
    static double add(double a, double b) {
        System.out.println("  [Called: add(double, double)]");
        return a + b;
    }

    // Overload 3: Three integers
    static int add(int a, int b, int c) {
        System.out.println("  [Called: add(int, int, int)]");
        return a + b + c;
    }

    // Overload 4: Two longs
    static long add(long a, long b) {
        System.out.println("  [Called: add(long, long)]");
        return a + b;
    }
}
public class AddOverload {
    public static void main(String[] args) {
        System.out.println("=== Method Overloading: add() ===\n");

        // Integer addition
        int left = ;
        int right = ;
        int intResult = add(left, right);
        System.out.println("add(" + left + ", " + right + ") = " + intResult);

        // Double addition
        double doubleResult = add(5.5, 3.2);
        System.out.println("add(5.5, 3.2) = " + doubleResult);

        // Three integers
        int tripleResult = add(10, 20, 30);
        System.out.println("add(10, 20, 30) = " + tripleResult);

        // More types
        long longResult = add(1000000000L, 2000000000L);
        System.out.println("add(1000000000L, 2000000000L) = " + longResult);

        // Compiler chooses the right method!
        System.out.println("\n=== Compiler Decision ===");
        System.out.println("add(1, 2)     → int version");
        System.out.println("add(1.0, 2.0) → double version");
        System.out.println("add(1, 2, 3)  → three-param version");
    }

    // Overload 1: Two integers
    static int add(int a, int b) {
        System.out.println("  [Called: add(int, int)]");
        return a + b;
    }

    // Overload 2: Two doubles
    static double add(double a, double b) {
        System.out.println("  [Called: add(double, double)]");
        return a + b;
    }

    // Overload 3: Three integers
    static int add(int a, int b, int c) {
        System.out.println("  [Called: add(int, int, int)]");
        return a + b + c;
    }

    // Overload 4: Two longs
    static long add(long a, long b) {
        System.out.println("  [Called: add(long, long)]");
        return a + b;
    }
}
public class AddOverload {
    public static void main(String[] args) {
        System.out.println("=== Method Overloading: add() ===\n");

        // Integer addition
        int left = ;
        int right = ;
        int intResult = add(left, right);
        System.out.println("add(" + left + ", " + right + ") = " + intResult);

        // Double addition
        double doubleResult = add(5.5, 3.2);
        System.out.println("add(5.5, 3.2) = " + doubleResult);

        // Three integers
        int tripleResult = add(10, 20, 30);
        System.out.println("add(10, 20, 30) = " + tripleResult);

        // More types
        long longResult = add(1000000000L, 2000000000L);
        System.out.println("add(1000000000L, 2000000000L) = " + longResult);

        // Compiler chooses the right method!
        System.out.println("\n=== Compiler Decision ===");
        System.out.println("add(1, 2)     → int version");
        System.out.println("add(1.0, 2.0) → double version");
        System.out.println("add(1, 2, 3)  → three-param version");
    }

    // Overload 1: Two integers
    static int add(int a, int b) {
        System.out.println("  [Called: add(int, int)]");
        return a + b;
    }

    // Overload 2: Two doubles
    static double add(double a, double b) {
        System.out.println("  [Called: add(double, double)]");
        return a + b;
    }

    // Overload 3: Three integers
    static int add(int a, int b, int c) {
        System.out.println("  [Called: add(int, int, int)]");
        return a + b + c;
    }

    // Overload 4: Two longs
    static long add(long a, long b) {
        System.out.println("  [Called: add(long, long)]");
        return a + b;
    }
}
public class AddOverload {
    public static void main(String[] args) {
        System.out.println("=== Method Overloading: add() ===\n");

        // Integer addition
        int left = ;
        int right = ;
        int intResult = add(left, right);
        System.out.println("add(" + left + ", " + right + ") = " + intResult);

        // Double addition
        double doubleResult = add(5.5, 3.2);
        System.out.println("add(5.5, 3.2) = " + doubleResult);

        // Three integers
        int tripleResult = add(10, 20, 30);
        System.out.println("add(10, 20, 30) = " + tripleResult);

        // More types
        long longResult = add(1000000000L, 2000000000L);
        System.out.println("add(1000000000L, 2000000000L) = " + longResult);

        // Compiler chooses the right method!
        System.out.println("\n=== Compiler Decision ===");
        System.out.println("add(1, 2)     → int version");
        System.out.println("add(1.0, 2.0) → double version");
        System.out.println("add(1, 2, 3)  → three-param version");
    }

    // Overload 1: Two integers
    static int add(int a, int b) {
        System.out.println("  [Called: add(int, int)]");
        return a + b;
    }

    // Overload 2: Two doubles
    static double add(double a, double b) {
        System.out.println("  [Called: add(double, double)]");
        return a + b;
    }

    // Overload 3: Three integers
    static int add(int a, int b, int c) {
        System.out.println("  [Called: add(int, int, int)]");
        return a + b + c;
    }

    // Overload 4: Two longs
    static long add(long a, long b) {
        System.out.println("  [Called: add(long, long)]");
        return a + b;
    }
}
public class AddOverload {
    public static void main(String[] args) {
        System.out.println("=== Method Overloading: add() ===\n");

        // Integer addition
        int left = ;
        int right = ;
        int intResult = add(left, right);
        System.out.println("add(" + left + ", " + right + ") = " + intResult);

        // Double addition
        double doubleResult = add(5.5, 3.2);
        System.out.println("add(5.5, 3.2) = " + doubleResult);

        // Three integers
        int tripleResult = add(10, 20, 30);
        System.out.println("add(10, 20, 30) = " + tripleResult);

        // More types
        long longResult = add(1000000000L, 2000000000L);
        System.out.println("add(1000000000L, 2000000000L) = " + longResult);

        // Compiler chooses the right method!
        System.out.println("\n=== Compiler Decision ===");
        System.out.println("add(1, 2)     → int version");
        System.out.println("add(1.0, 2.0) → double version");
        System.out.println("add(1, 2, 3)  → three-param version");
    }

    // Overload 1: Two integers
    static int add(int a, int b) {
        System.out.println("  [Called: add(int, int)]");
        return a + b;
    }

    // Overload 2: Two doubles
    static double add(double a, double b) {
        System.out.println("  [Called: add(double, double)]");
        return a + b;
    }

    // Overload 3: Three integers
    static int add(int a, int b, int c) {
        System.out.println("  [Called: add(int, int, int)]");
        return a + b + c;
    }

    // Overload 4: Two longs
    static long add(long a, long b) {
        System.out.println("  [Called: add(long, long)]");
        return a + b;
    }
}
public class AddOverload {
    public static void main(String[] args) {
        System.out.println("=== Method Overloading: add() ===\n");

        // Integer addition
        int left = ;
        int right = ;
        int intResult = add(left, right);
        System.out.println("add(" + left + ", " + right + ") = " + intResult);

        // Double addition
        double doubleResult = add(5.5, 3.2);
        System.out.println("add(5.5, 3.2) = " + doubleResult);

        // Three integers
        int tripleResult = add(10, 20, 30);
        System.out.println("add(10, 20, 30) = " + tripleResult);

        // More types
        long longResult = add(1000000000L, 2000000000L);
        System.out.println("add(1000000000L, 2000000000L) = " + longResult);

        // Compiler chooses the right method!
        System.out.println("\n=== Compiler Decision ===");
        System.out.println("add(1, 2)     → int version");
        System.out.println("add(1.0, 2.0) → double version");
        System.out.println("add(1, 2, 3)  → three-param version");
    }

    // Overload 1: Two integers
    static int add(int a, int b) {
        System.out.println("  [Called: add(int, int)]");
        return a + b;
    }

    // Overload 2: Two doubles
    static double add(double a, double b) {
        System.out.println("  [Called: add(double, double)]");
        return a + b;
    }

    // Overload 3: Three integers
    static int add(int a, int b, int c) {
        System.out.println("  [Called: add(int, int, int)]");
        return a + b + c;
    }

    // Overload 4: Two longs
    static long add(long a, long b) {
        System.out.println("  [Called: add(long, long)]");
        return a + b;
    }
}

Same name, different types: add(int, int) vs add(double, double).

method overloading Multiple methods with same name but different parameter lists.

Overload for multiple data types

Handle different input types with the same method name.

PrintOverload.java
public class PrintOverload {
    public static void main(String[] args) {
        System.out.println("=== Overloaded print() ===\n");

        // Different types, same method name
        print(42);                          // int
        print(3.14159);                     // double
        print("Hello World");               // String
        print(true);                        // boolean

        // Arrays
        print(new int[]{1, 2, 3, 4, 5});    // int array
        print(new String[]{"a", "b", "c"}); // String array

        System.out.println("\n=== How Java's System.out Works ===");
        System.out.println("System.out has 10+ overloaded println() methods!");
        System.out.println("println(int), println(double), println(String), etc.");
    }

    // Overload for int
    static void print(int value) {
        System.out.println("Integer: " + value);
    }

    // Overload for double
    static void print(double value) {
        System.out.println("Double: " + value);
    }

    // Overload for String
    static void print(String value) {
        System.out.println("String: \"" + value + "\"");
    }

    // Overload for boolean
    static void print(boolean value) {
        System.out.println("Boolean: " + value);
    }

    // Overload for int array
    static void print(int[] arr) {
        System.out.print("Int Array: [");
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]);
            if (i < arr.length - 1) System.out.print(", ");
        }
        System.out.println("]");
    }

    // Overload for String array
    static void print(String[] arr) {
        System.out.print("String Array: [");
        for (int i = 0; i < arr.length; i++) {
            System.out.print("\"" + arr[i] + "\"");
            if (i < arr.length - 1) System.out.print(", ");
        }
        System.out.println("]");
    }
}

The compiler picks the right version based on argument types.

Overload for different argument counts

Provide flexible APIs with varying parameter counts.

MaxOverload.java
public class MaxOverload {
    public static void main(String[] args) {
        System.out.println("=== Overloaded max() ===\n");

        // Two arguments
        System.out.println("max(10, 20) = " + max(10, 20));
        System.out.println("max(3.5, 2.8) = " + max(3.5, 2.8));

        // Three arguments
        System.out.println("max(5, 12, 8) = " + max(5, 12, 8));
        System.out.println("max(1.1, 2.2, 3.3) = " + max(1.1, 2.2, 3.3));

        // Four arguments
        System.out.println("max(4, 7, 2, 9) = " + max(4, 7, 2, 9));

        // Compare with Java's Math.max
        System.out.println("\n=== Java's Math.max ===");
        System.out.println("Math.max(10, 20) = " + Math.max(10, 20));
        System.out.println("Math.max only takes 2 args - chain for more:");
        System.out.println("Math.max(Math.max(5, 12), 8) = " +
            Math.max(Math.max(5, 12), 8));

        // Min versions
        System.out.println("\n=== Overloaded min() ===");
        System.out.println("min(10, 20) = " + min(10, 20));
        System.out.println("min(5, 12, 8) = " + min(5, 12, 8));
    }

    // Two ints
    static int max(int a, int b) {
        return (a > b) ? a : b;
    }

    // Two doubles
    static double max(double a, double b) {
        return (a > b) ? a : b;
    }

    // Three ints
    static int max(int a, int b, int c) {
        return max(max(a, b), c);  // Reuse two-arg version!
    }

    // Three doubles
    static double max(double a, double b, double c) {
        return max(max(a, b), c);
    }

    // Four ints
    static int max(int a, int b, int c, int d) {
        return max(max(a, b), max(c, d));
    }

    // Min versions
    static int min(int a, int b) {
        return (a < b) ? a : b;
    }

    static int min(int a, int b, int c) {
        return min(min(a, b), c);
    }
}

max(a, b) and max(a, b, c) - same concept, different arity.

Constructor overloading

Create objects in multiple ways using overloaded constructors.

ConstructorOverload.java
public class ConstructorOverload {
    public static void main(String[] args) {
        System.out.println("=== Constructor Overloading ===\n");

        // Different ways to create a Book
        Book book1 = new Book();
        System.out.println("Default: " + book1);

        Book book2 = new Book("1984");
        System.out.println("Title only: " + book2);

        Book book3 = new Book("1984", "George Orwell");
        System.out.println("Title + Author: " + book3);

        Book book4 = new Book("1984", "George Orwell", 328);
        System.out.println("All params: " + book4);

        // Copy constructor
        Book book5 = new Book(book4);
        System.out.println("Copy: " + book5);

        System.out.println("\n=== Flexibility of Overloading ===");
        System.out.println("Same class, 5 ways to create objects!");
    }
}

class Book {
    String title;
    String author;
    int pages;

    // Constructor 1: No arguments (defaults)
    Book() {
        this("Unknown Title", "Unknown Author", 0);  // Calls another constructor
    }

    // Constructor 2: Title only
    Book(String title) {
        this(title, "Unknown Author", 0);
    }

    // Constructor 3: Title and author
    Book(String title, String author) {
        this(title, author, 0);
    }

    // Constructor 4: All parameters (primary)
    Book(String title, String author, int pages) {
        this.title = title;
        this.author = author;
        this.pages = pages;
    }

    // Constructor 5: Copy constructor
    Book(Book other) {
        this(other.title, other.author, other.pages);
    }

    @Override
    public String toString() {
        return "\"" + title + "\" by " + author +
               (pages > 0 ? " (" + pages + " pages)" : "");
    }
}

Constructors can be overloaded just like regular methods.

constructor overloading Multiple constructors with different parameters for flexible object creation.

Avoid ambiguity

Understand when overloading becomes confusing.

Ambiguity.java
public class Ambiguity {
    public static void main(String[] args) {
        System.out.println("=== Overloading Ambiguity ===\n");

        // Clear cases - no ambiguity
        System.out.println("=== Clear Cases ===");
        process(10);           // Clearly int
        process("Hello");      // Clearly String
        process(10, 20);       // Clearly two ints

        // Ambiguous case with null
        System.out.println("\n=== Null Ambiguity ===");
        // process(null);      // Error! Both String and int[] match null

        // Solution: explicit cast
        process((String) null);    // Explicit: String version
        process((int[]) null);     // Explicit: int[] version

        // Type promotion ambiguity
        System.out.println("\n=== Type Promotion ===");
        demo(10);         // int → could go to long or double?
        demo(10L);        // Explicitly long
        demo(10.0);       // Explicitly double

        System.out.println("\n=== Rules ===");
        System.out.println("1. Exact match is preferred");
        System.out.println("2. Widening (int→long→double) preferred over boxing");
        System.out.println("3. null can match any reference type - be explicit!");
    }

    // Overload 1: int
    static void process(int x) {
        System.out.println("process(int): " + x);
    }

    // Overload 2: String
    static void process(String s) {
        System.out.println("process(String): " + s);
    }

    // Overload 3: two ints
    static void process(int x, int y) {
        System.out.println("process(int, int): " + x + ", " + y);
    }

    // Overload 4: int array
    static void process(int[] arr) {
        System.out.println("process(int[]): array");
    }

    // Demo type promotion
    static void demo(int x) {
        System.out.println("demo(int): " + x);
    }

    static void demo(long x) {
        System.out.println("demo(long): " + x);
    }

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

Some combinations create ambiguous calls. Know the rules to avoid compiler errors.

Exercise: TypePromotion.java

Explore how Java's type promotion affects overload resolution