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 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 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
int add(int a, int b) and long add(int a, int b) won't compile โ the parameter list must differ.print(int... a) and print(int a, int... b) cause compiler ambiguity.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 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
| Feature | Syntax | Use Case |
|---|---|---|
| Overloading | add(int), add(double) | Same operation, different types |
| Varargs | void print(String... args) | Flexible argument count |
| Method ref | String::toUpperCase | Pass method as lambda |
| @FunctionalInterface | Single abstract method | Lambda compatibility |
| Recursion | method calls itself | Tree/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.