Courses/Java/Advanced Methods

    Lesson 15 โ€ข Advanced

    Advanced Methods & Overloading

    Master method overloading, varargs, method references, and lambda-compatible signatures.

    ๐Ÿ“š Before You Start

    You should be comfortable with:

    • Methods (Lesson 6) โ€” parameters, return types, calling methods
    • Generics (Lesson 14) โ€” type parameters and bounds
    • Interfaces (Lesson 11) โ€” contracts and polymorphism

    What You'll Learn

    • โœ… Method overloading rules and best practices
    • โœ… Varargs (variable-length arguments) usage
    • โœ… Method references with :: operator
    • โœ… Functional interfaces and lambda compatibility
    • โœ… Static vs instance method selection
    • โœ… Recursive methods and tail recursion

    1๏ธโƒฃ Method Overloading Deep Dive

    Method overloading lets you define multiple methods with the same name but different parameter lists. The compiler picks the most specific match โ€” this is called static dispatch.

    ๐Ÿ’ก Real-World Analogy

    Think of a restaurant that offers the same dish in different sizes: small, medium, large. The name is the same ("pasta"), but the parameters (size, toppings) differ.

    How the Compiler Chooses

    Java uses a three-phase process: (1) try exact match, (2) try widening conversions (int โ†’ long), (3) try autoboxing (int โ†’ Integer). If two methods are equally specific, you get a compile error.

    // โœ… Valid overloading โ€” different parameter types/counts
    public int add(int a, int b) { return a + b; }
    public double add(double a, double b) { return a + b; }
    public int add(int a, int b, int c) { return a + b + c; }
    
    // โŒ NOT valid โ€” return type alone doesn't differentiate
    // public long add(int a, int b) { return a + b; } // Compile error!

    2๏ธโƒฃ Varargs: Flexible Parameter Lists

    Varargs (Type... args) let a method accept zero or more arguments. Under the hood, Java creates an array. Varargs must always be the last parameter.

    public static int sum(int... numbers) {
        int total = 0;
        for (int n : numbers) total += n;
        return total;
    }
    
    sum(1, 2, 3);       // 6
    sum(10, 20, 30, 40); // 100
    sum();               // 0 โ€” zero args is valid!

    Try It: Overloading & Varargs

    See how method overloading and varargs work in practice

    Try it Yourself ยป
    JavaScript
    // ๐Ÿ’ก Try modifying this code and see what happens!
    // Method Overloading & Varargs (Simulated)
    console.log("=== Method Overloading ===\n");
    
    // Overloading with different parameter counts
    function calculate(a, b, c) {
        if (c !== undefined) {
            console.log("3 params: " + a + " + " + b + " + " + c + " = " + (a + b + c));
            return a + b + c;
        } else if (b !== undefined) {
            console.log("2 params: " + a + " + " + b + " = " + (a + b));
            return a + b;
        } else {
         
    ...

    3๏ธโƒฃ Method References (::)

    Method references are shorthand for lambdas that just call an existing method. Four types:

    // Static reference
    Function<Double, Double> sqrt = Math::sqrt;
    
    // Instance on specific object
    Consumer<String> printer = System.out::println;
    
    // Instance on arbitrary object
    Function<String, String> upper = String::toUpperCase;
    
    // Constructor reference
    Supplier<ArrayList> listMaker = ArrayList::new;

    4๏ธโƒฃ Recursive Methods

    Analogy: Recursion is like Russian nesting dolls โ€” each doll contains a smaller version of itself until you reach the smallest one (base case).

    public static int factorial(int n) {
        if (n <= 1) return 1;        // base case
        return n * factorial(n - 1); // recursive call
    }
    
    // factorial(5) โ†’ 5 * 4 * 3 * 2 * 1 = 120

    โš ๏ธ Warning: The JVM doesn't optimize tail calls. For large inputs, convert recursion to iteration.

    Try It: Method References & Lambdas

    Use functional programming patterns with method references

    Try it Yourself ยป
    JavaScript
    // ๐Ÿ’ก Try modifying this code and see what happens!
    // Method References & Lambdas (Simulated)
    console.log("=== Method References ===\n");
    
    // 1. Using function references
    let names = ["Alice", "bob", "Charlie", "dave"];
    console.log("1. Original:", names);
    let upper = names.map(s => s.toUpperCase());
    console.log("   toUpperCase:", upper);
    
    // 2. Using function as reference
    function isPalindrome(str) {
        let clean = str.toLowerCase();
        return clean === clean.split("").reverse().join("");
    }
    l
    ...

    5๏ธโƒฃ Common Mistakes

    โŒ
    Changing only return type: int add(int a, int b) and long add(int a, int b) won't compile โ€” the parameter list must differ.
    โŒ
    Ambiguous varargs: print(int... a) and print(int a, int... b) cause compiler ambiguity.
    โŒ
    Confusing overloading with overriding: Overloading is compile-time (same class, different params). Overriding is runtime (subclass, same signature).
    โŒ
    Deep recursion without base case: Missing base cases cause StackOverflowError.

    6๏ธโƒฃ Pro Tips

    ๐Ÿ’ก Prefer method references over lambdas when the lambda just delegates: list.forEach(System.out::println).

    ๐Ÿ’ก Use @FunctionalInterface on custom interfaces to enforce the single abstract method contract.

    ๐Ÿ’ก Avoid deep recursion in Java โ€” convert to iteration or use a Stack data structure for large inputs.

    ๐Ÿ’ก Overload sparingly โ€” too many overloads confuse other developers. Consider a builder pattern instead.

    Try It: Recursion Patterns

    Practice recursive algorithms: factorial, fibonacci, and tree traversal

    Try it Yourself ยป
    JavaScript
    // ๐Ÿ’ก Try modifying this code and see what happens!
    // Recursion Patterns (Simulated)
    console.log("=== Recursion ===\n");
    
    // 1. Factorial
    console.log("1. Factorial:");
    function factorial(n) {
        if (n <= 1) return 1;
        return n * factorial(n - 1);
    }
    for (let i = 1; i <= 8; i++) {
        console.log("   " + i + "! = " + factorial(i));
    }
    
    // 2. Fibonacci
    console.log("\n2. Fibonacci:");
    function fibonacci(n) {
        if (n <= 1) return n;
        return fibonacci(n - 1) + fibonacci(n - 2);
    }
    let fibs = [
    ...

    ๐Ÿ“‹ Quick Reference

    FeatureSyntaxUse Case
    Overloadingadd(int), add(double)Same operation, different types
    Varargsvoid print(String... args)Flexible argument count
    Method refString::toUpperCasePass method as lambda
    @FunctionalInterfaceSingle abstract methodLambda compatibility
    Recursionmethod calls itselfTree/divide-and-conquer

    ๐ŸŽ‰ Lesson Complete!

    You've mastered advanced method techniques! You can now write flexible, reusable methods using overloading, varargs, method references, and recursion.

    Next up: Deep Dive into Java OOP Design Patterns โ€” learn Singleton, Factory, Builder, and Decorator patterns!

    Sign up for free to track which lessons you've completed and get learning reminders.

    Previous

    Cookie & Privacy Settings

    We use cookies to improve your experience, analyze traffic, and show personalized ads. You can manage your preferences below.

    By clicking "Accept All", you consent to our use of cookies for analytics and personalized advertising. You can customize your preferences or reject non-essential cookies.

    Privacy Policy โ€ข Terms of Service