Method and Constructor Overloading#
Lecture 15#
Java Programming (4343203)
Diploma in ICT - Semester IV
Gujarat Technological University
layout: default#
Learning Objectives#
By the end of this lecture, you will be able to:
- ๐ฏ Understand the concept and benefits of method overloading
- ๐๏ธ Implement constructor overloading for flexible object creation
- ๐ Apply overloading resolution rules correctly
- โก Design flexible APIs using overloading techniques
- ๐จ Create user-friendly method interfaces
- ๐ Practice with real-world overloading scenarios
layout: center#
What is Overloading?#
graph TD
A[Overloading] --> B[Same Method Name]
A --> C[Different Parameters]
A --> D[Compile-time Decision]
B --> E[Method Overloading]
B --> F[Constructor Overloading]
C --> G[Different Number]
C --> H[Different Types]
C --> I[Different Order]
D --> J[Static Polymorphism]
D --> K[Early Binding]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
๐ฏ Overloading Benefits
- โข Same logical operation, different inputs
- โข Improved code readability
- โข Flexible method interfaces
- โข Reduced learning curve for APIs
๐ง Key Rules
- โข Same method name required
- โข Different parameter lists
- โข Return type can vary
- โข Access modifiers can differ
layout: default#
Method Overloading Fundamentals#
๐ฏ Basic Method Overloading#
public class Calculator {
// Method overloading with different number of parameters
public int add(int a, int b) {
System.out.println("Adding two integers: " + a + " + " + b);
return a + b;
}
public int add(int a, int b, int c) {
System.out.println("Adding three integers: " + a + " + " + b + " + " + c);
return a + b + c;
}
public int add(int a, int b, int c, int d) {
System.out.println("Adding four integers");
return a + b + c + d;
}
// Method overloading with different parameter types
public double add(double a, double b) {
System.out.println("Adding two doubles: " + a + " + " + b);
return a + b;
}
public float add(float a, float b) {
System.out.println("Adding two floats: " + a + " + " + b);
return a + b;
}
// Method overloading with different parameter order
public String add(String a, int b) {
System.out.println("Concatenating string and int: " + a + " + " + b);
return a + b;
}
public String add(int a, String b) {
System.out.println("Concatenating int and string: " + a + " + " + b);
return a + b;
}
}
๐ Overloading Resolution Demo#
public class CalculatorDemo {
public static void main(String[] args) {
Calculator calc = new Calculator();
// Different methods called based on parameters
// Calls add(int, int)
int result1 = calc.add(5, 10);
System.out.println("Result 1: " + result1);
// Calls add(int, int, int)
int result2 = calc.add(5, 10, 15);
System.out.println("Result 2: " + result2);
// Calls add(double, double)
double result3 = calc.add(5.5, 10.7);
System.out.println("Result 3: " + result3);
// Calls add(float, float)
float result4 = calc.add(5.5f, 10.7f);
System.out.println("Result 4: " + result4);
// Calls add(String, int)
String result5 = calc.add("Number: ", 42);
System.out.println("Result 5: " + result5);
// Calls add(int, String)
String result6 = calc.add(42, " is the answer");
System.out.println("Result 6: " + result6);
// Automatic type promotion example
byte a = 5, b = 10;
int result7 = calc.add(a, b); // byte promoted to int
System.out.println("Result 7: " + result7);
}
}
Output demonstrates which method is called for each parameter combination.
layout: default#
Advanced Method Overloading#
๐ Array and Varargs Overloading#
public class DataProcessor {
// Method with array parameter
public int sum(int[] numbers) {
System.out.println("Processing array of " + numbers.length + " elements");
int total = 0;
for (int num : numbers) {
total += num;
}
return total;
}
// Method with varargs parameter
public int sum(int... numbers) {
System.out.println("Processing varargs with " + numbers.length + " elements");
int total = 0;
for (int num : numbers) {
total += num;
}
return total;
}
// Method with specific number of parameters
public int sum(int a, int b) {
System.out.println("Processing two specific integers");
return a + b;
}
// Method with array and additional parameter
public double average(int[] numbers, boolean includeZeros) {
System.out.println("Calculating average with zero handling: " + includeZeros);
if (!includeZeros) {
// Filter out zeros
int count = 0;
int sum = 0;
for (int num : numbers) {
if (num != 0) {
sum += num;
count++;
}
}
return count > 0 ? (double) sum / count : 0.0;
} else {
return (double) sum(numbers) / numbers.length;
}
}
// Method with varargs and additional parameter
public double average(boolean includeZeros, int... numbers) {
System.out.println("Varargs average with zero handling: " + includeZeros);
return average(numbers, includeZeros);
}
}
๐ฏ Generic Type Overloading#
public class GenericProcessor {
// Overloading with different generic types
public <T> void process(T item) {
System.out.println("Processing single item: " + item);
System.out.println("Type: " + item.getClass().getSimpleName());
}
public <T> void process(T[] items) {
System.out.println("Processing array of " + items.length + " items");
for (T item : items) {
System.out.println(" - " + item);
}
}
public <T> void process(java.util.List<T> items) {
System.out.println("Processing list of " + items.size() + " items");
for (int i = 0; i < items.size(); i++) {
System.out.println(" [" + i + "] " + items.get(i));
}
}
// Specific type overloads for optimization
public void process(String text) {
System.out.println("Optimized string processing: " + text.toUpperCase());
}
public void process(Integer number) {
System.out.println("Optimized integer processing: " + (number * 2));
}
public void process(Double decimal) {
System.out.println("Optimized double processing: " + String.format("%.2f", decimal));
}
}
// Usage demonstration
GenericProcessor processor = new GenericProcessor();
processor.process("Hello World"); // Calls String version
processor.process(42); // Calls Integer version
processor.process(3.14159); // Calls Double version
processor.process(new Boolean(true)); // Calls generic version
String[] stringArray = {"A", "B", "C"};
processor.process(stringArray); // Calls array version
List<String> stringList = Arrays.asList("X", "Y", "Z");
processor.process(stringList); // Calls list version
layout: default#
Constructor Overloading Deep Dive#
๐๏ธ Comprehensive Constructor Design#
public class Person {
private String firstName;
private String lastName;
private int age;
private String email;
private String phone;
private String address;
private String country;
// Default constructor
public Person() {
this("Unknown", "Person", 0);
System.out.println("Default constructor called");
}
// Essential information constructor
public Person(String firstName, String lastName, int age) {
this.firstName = validateName(firstName);
this.lastName = validateName(lastName);
this.age = validateAge(age);
this.email = generateEmail();
this.phone = "";
this.address = "";
this.country = "Unknown";
System.out.println("Basic constructor called");
}
// Constructor with contact information
public Person(String firstName, String lastName, int age,
String email, String phone) {
this(firstName, lastName, age); // Chain to basic constructor
this.email = validateEmail(email);
this.phone = validatePhone(phone);
System.out.println("Contact info constructor called");
}
// Full constructor
public Person(String firstName, String lastName, int age,
String email, String phone, String address, String country) {
this(firstName, lastName, age, email, phone); // Chain to contact constructor
this.address = address != null ? address : "";
this.country = country != null ? country : "Unknown";
System.out.println("Full constructor called");
}
}
๐ง Validation and Utility Methods#
// Copy constructor
public Person(Person other) {
if (other == null) {
throw new IllegalArgumentException("Cannot copy null person");
}
this.firstName = other.firstName;
this.lastName = other.lastName;
this.age = other.age;
this.email = other.email;
this.phone = other.phone;
this.address = other.address;
this.country = other.country;
System.out.println("Copy constructor called");
}
// Validation methods
private String validateName(String name) {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("Name cannot be null or empty");
}
return name.trim();
}
private int validateAge(int age) {
if (age < 0 || age > 150) {
throw new IllegalArgumentException("Age must be between 0 and 150");
}
return age;
}
private String validateEmail(String email) {
if (email == null || !email.contains("@")) {
return generateEmail(); // Generate default if invalid
}
return email;
}
private String validatePhone(String phone) {
if (phone == null) {
return "";
}
// Remove non-digits and validate
String cleaned = phone.replaceAll("[^0-9]", "");
return cleaned.length() >= 10 ? cleaned : "";
}
private String generateEmail() {
return firstName.toLowerCase() + "." + lastName.toLowerCase() + "@example.com";
}
// Display method
public void displayInfo() {
System.out.println("=== Person Information ===");
System.out.println("Name: " + firstName + " " + lastName);
System.out.println("Age: " + age);
System.out.println("Email: " + email);
System.out.println("Phone: " + phone);
System.out.println("Address: " + address);
System.out.println("Country: " + country);
}
}
layout: default#
Overloading Resolution Rules#
๐ฏ Method Resolution Priority#
1. Exact Match
- Parameters match exactly in type and number
2. Promotion
- Automatic widening of primitive types
3. Autoboxing/Unboxing
- Primitive to wrapper or wrapper to primitive
4. Varargs
- Variable arguments as last resort
5. Compilation Error
- If multiple matches at same level
๐ Promotion Hierarchy#
byte โ short โ int โ long โ float โ double
char โ int โ long โ float โ double
๐ Resolution Examples#
public class ResolutionDemo {
public void test(int x) {
System.out.println("int version: " + x);
}
public void test(long x) {
System.out.println("long version: " + x);
}
public void test(double x) {
System.out.println("double version: " + x);
}
public void test(Integer x) {
System.out.println("Integer wrapper version: " + x);
}
public void test(int... x) {
System.out.println("varargs version with " + x.length + " arguments");
}
public static void main(String[] args) {
ResolutionDemo demo = new ResolutionDemo();
demo.test(5); // int version (exact match)
demo.test(5L); // long version (exact match)
demo.test(5.0); // double version (exact match)
byte b = 5;
demo.test(b); // int version (promotion)
short s = 5;
demo.test(s); // int version (promotion)
char c = '5';
demo.test(c); // int version (promotion)
float f = 5.0f;
demo.test(f); // double version (promotion)
Integer wrapper = 5;
demo.test(wrapper); // Integer version (exact match)
demo.test(1, 2, 3); // varargs version
demo.test(); // varargs version (empty)
}
}
layout: default#
Real-World Example: Database Connection Manager#
๐๏ธ Flexible Connection API#
public class DatabaseManager {
private String host;
private int port;
private String database;
private String username;
private String password;
private String connectionString;
// Default local connection
public DatabaseManager() {
this("localhost", 3306, "testdb");
System.out.println("Created local development connection");
}
// Host and database only
public DatabaseManager(String host, String database) {
this(host, 3306, database, "root", "");
System.out.println("Created connection with default port and credentials");
}
// Host, port, and database
public DatabaseManager(String host, int port, String database) {
this(host, port, database, "root", "");
System.out.println("Created connection with default credentials");
}
// Full credentials
public DatabaseManager(String host, int port, String database,
String username, String password) {
this.host = validateHost(host);
this.port = validatePort(port);
this.database = validateDatabase(database);
this.username = username != null ? username : "root";
this.password = password != null ? password : "";
this.connectionString = buildConnectionString();
System.out.println("Created connection with full credentials");
}
// Connection string constructor
public DatabaseManager(String connectionString) {
this.connectionString = validateConnectionString(connectionString);
parseConnectionString();
System.out.println("Created connection from connection string");
}
}
๐ง Connection Methods Overloading#
// Connect methods with different authentication options
public boolean connect() {
return connect(this.username, this.password);
}
public boolean connect(String username, String password) {
System.out.println("Connecting to " + host + ":" + port + "/" + database);
System.out.println("Username: " + username);
// Simulate connection logic
try {
Thread.sleep(100); // Simulate connection delay
System.out.println("Connection successful!");
return true;
} catch (InterruptedException e) {
System.out.println("Connection failed: " + e.getMessage());
return false;
}
}
// Overloaded query methods
public void executeQuery(String sql) {
executeQuery(sql, new Object[0]);
}
public void executeQuery(String sql, Object... parameters) {
System.out.println("Executing query: " + sql);
if (parameters.length > 0) {
System.out.println("Parameters:");
for (int i = 0; i < parameters.length; i++) {
System.out.println(" [" + i + "] " + parameters[i]);
}
}
}
public void executeQuery(String sql, java.util.Map<String, Object> namedParameters) {
System.out.println("Executing query with named parameters: " + sql);
namedParameters.forEach((key, value) ->
System.out.println(" " + key + " = " + value));
}
// Validation methods
private String validateHost(String host) {
if (host == null || host.trim().isEmpty()) {
throw new IllegalArgumentException("Host cannot be null or empty");
}
return host.trim();
}
private int validatePort(int port) {
if (port < 1 || port > 65535) {
throw new IllegalArgumentException("Port must be between 1 and 65535");
}
return port;
}
private String validateDatabase(String database) {
if (database == null || database.trim().isEmpty()) {
throw new IllegalArgumentException("Database name cannot be null or empty");
}
return database.trim();
}
}
layout: default#
Overloading with Collections and Generics#
๐ฆ Collection Processing Overloads#
import java.util.*;
public class CollectionProcessor<T> {
// Single item processing
public void process(T item) {
System.out.println("Processing single item: " + item);
performOperation(item);
}
// Array processing
public void process(T[] items) {
System.out.println("Processing array of " + items.length + " items");
for (T item : items) {
performOperation(item);
}
}
// List processing
public void process(List<T> items) {
System.out.println("Processing List of " + items.size() + " items");
for (int i = 0; i < items.size(); i++) {
System.out.println("Index " + i + ": ");
performOperation(items.get(i));
}
}
// Set processing (unique items)
public void process(Set<T> items) {
System.out.println("Processing Set of " + items.size() + " unique items");
items.forEach(this::performOperation);
}
// Map processing (key-value pairs)
public void process(Map<String, T> items) {
System.out.println("Processing Map of " + items.size() + " key-value pairs");
items.forEach((key, value) -> {
System.out.println("Key: " + key);
performOperation(value);
});
}
// Varargs processing
public void process(T... items) {
System.out.println("Processing varargs of " + items.length + " items");
for (T item : items) {
performOperation(item);
}
}
}
๐ฏ Specialized Processing Methods#
// Processing with filtering
public void process(List<T> items, Predicate<T> filter) {
System.out.println("Processing List with filter");
items.stream()
.filter(filter)
.forEach(this::performOperation);
}
// Processing with transformation
public <R> List<R> process(List<T> items, Function<T, R> transformer) {
System.out.println("Processing List with transformation");
return items.stream()
.map(transformer)
.collect(Collectors.toList());
}
// Batch processing with size limit
public void process(List<T> items, int batchSize) {
System.out.println("Processing List in batches of " + batchSize);
for (int i = 0; i < items.size(); i += batchSize) {
int endIndex = Math.min(i + batchSize, items.size());
List<T> batch = items.subList(i, endIndex);
System.out.println("Processing batch " + (i / batchSize + 1));
batch.forEach(this::performOperation);
}
}
// Concurrent processing
public void processParallel(List<T> items) {
System.out.println("Processing List in parallel");
items.parallelStream().forEach(this::performOperation);
}
// Helper method
private void performOperation(T item) {
System.out.println(" Operating on: " + item);
// Actual processing logic would go here
}
}
// Usage demonstration
CollectionProcessor<String> processor = new CollectionProcessor<>();
// Different collection types
processor.process("single item");
processor.process(new String[]{"A", "B", "C"});
processor.process(Arrays.asList("X", "Y", "Z"));
processor.process(new HashSet<>(Arrays.asList("P", "Q", "R")));
Map<String, String> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", "value2");
processor.process(map);
layout: default#
Ambiguous Overloading and Solutions#
โ ๏ธ Common Ambiguity Problems#
public class AmbiguityProblems {
// Problem 1: Ambiguous varargs
public void method(String... args) {
System.out.println("String varargs");
}
public void method(Object... args) {
System.out.println("Object varargs");
}
// Problem 2: Ambiguous autoboxing
public void process(Integer num) {
System.out.println("Integer version");
}
public void process(int... nums) {
System.out.println("int varargs version");
}
// Problem 3: Ambiguous generics
public void handle(List<String> list) {
System.out.println("String list");
}
// This causes compilation error due to type erasure
// public void handle(List<Integer> list) {
// System.out.println("Integer list");
// }
// Problem 4: Ambiguous with null
public void show(String text) {
System.out.println("String: " + text);
}
public void show(StringBuilder builder) {
System.out.println("StringBuilder: " + builder);
}
// Calling show(null) would be ambiguous!
}
โ Solutions to Ambiguity#
public class AmbiguitySolutions {
// Solution 1: Use specific method names
public void processStrings(String... args) {
System.out.println("Processing strings: " + Arrays.toString(args));
}
public void processObjects(Object... args) {
System.out.println("Processing objects: " + Arrays.toString(args));
}
// Solution 2: Add specific overloads
public void process(int num) {
System.out.println("Single int: " + num);
}
public void process(Integer num) {
System.out.println("Integer wrapper: " + num);
}
public void process(int... nums) {
System.out.println("Multiple ints: " + Arrays.toString(nums));
}
// Solution 3: Use different method names for generics
public void handleStringList(List<String> list) {
System.out.println("String list size: " + list.size());
}
public void handleIntegerList(List<Integer> list) {
System.out.println("Integer list size: " + list.size());
}
// Solution 4: Handle null cases explicitly
public void show(String text) {
if (text == null) {
System.out.println("Null string provided");
} else {
System.out.println("String: " + text);
}
}
public void show(StringBuilder builder) {
if (builder == null) {
System.out.println("Null StringBuilder provided");
} else {
System.out.println("StringBuilder: " + builder);
}
}
// Null-safe wrapper method
public void showSafely(Object obj) {
if (obj == null) {
System.out.println("Null object provided");
} else if (obj instanceof String) {
show((String) obj);
} else if (obj instanceof StringBuilder) {
show((StringBuilder) obj);
} else {
System.out.println("Unsupported type: " + obj.getClass());
}
}
}
layout: default#
Best Practices for Overloading#
โ Design Guidelines#
Logical Consistency:
- Same logical operation for all overloads
- Consistent naming and behavior
- Predictable parameter ordering
Parameter Design:
- Use specific types over generic when possible
- Avoid ambiguous parameter combinations
- Consider using builder pattern for many parameters
Documentation:
- Document parameter differences clearly
- Explain when to use each overload
- Provide usage examples
๐ฏ Implementation Examples#
public class WellDesignedAPI {
// Good: Consistent logical operation
public boolean save(String filename, String content) {
return save(filename, content, "UTF-8");
}
public boolean save(String filename, String content, String encoding) {
return save(new File(filename), content, encoding, false);
}
public boolean save(File file, String content, String encoding, boolean append) {
// Main implementation
System.out.println("Saving to: " + file.getPath());
System.out.println("Encoding: " + encoding);
System.out.println("Append: " + append);
return true;
}
// Good: Builder pattern for complex objects
public static class EmailBuilder {
private String to;
private String subject;
private String body;
private List<String> attachments = new ArrayList<>();
public EmailBuilder to(String to) { this.to = to; return this; }
public EmailBuilder subject(String subject) { this.subject = subject; return this; }
public EmailBuilder body(String body) { this.body = body; return this; }
public EmailBuilder attach(String... files) {
attachments.addAll(Arrays.asList(files));
return this;
}
public boolean send() {
// Validation and sending logic
System.out.println("Sending email to: " + to);
return true;
}
}
// Good: Clear method naming for different behaviors
public void sortAscending(int[] array) {
Arrays.sort(array);
System.out.println("Sorted ascending");
}
public void sortDescending(int[] array) {
Arrays.sort(array);
reverseArray(array);
System.out.println("Sorted descending");
}
private void reverseArray(int[] array) {
for (int i = 0; i < array.length / 2; i++) {
int temp = array[i];
array[i] = array[array.length - 1 - i];
array[array.length - 1 - i] = temp;
}
}
}
layout: default#
Practical Exercise: File Processor System#
๐ Design Challenge#
Requirements:
- Create a FileProcessor class with multiple overloaded methods
- Implement constructor overloading for different initialization options
- Add method overloading for file operations (read, write, copy)
- Handle different data types and formats
- Include validation and error handling
- Design a user-friendly API
public class FileProcessor {
// TODO: Instance variables for configuration
// TODO: Multiple constructors:
// - Default constructor
// - Constructor with base directory
// - Constructor with directory and encoding
// - Constructor with full configuration
// TODO: Overloaded read methods:
// - read(String filename)
// - read(File file)
// - read(String filename, String encoding)
// - read(File file, String encoding)
// TODO: Overloaded write methods for different data types
// TODO: Overloaded copy methods with various options
}
๐ฏ Expected Implementation#
Features to Implement:
- Constructor overloading with proper chaining
- Method overloading for file operations
- Support for different encodings and formats
- Varargs for batch operations
- Generic methods for different data types
- Proper validation and error handling
Success Criteria:
- All overloading rules correctly applied
- No ambiguous method calls
- Clean and intuitive API design
- Proper constructor chaining
- Comprehensive validation
- Real-world usability
Usage Examples:
// Constructor overloading
FileProcessor processor1 = new FileProcessor();
FileProcessor processor2 = new FileProcessor("/data");
FileProcessor processor3 = new FileProcessor("/data", "UTF-8");
// Method overloading
String content = processor1.read("file.txt");
processor1.write("output.txt", content);
processor1.copy("source.txt", "destination.txt");
processor1.processBatch("file1.txt", "file2.txt", "file3.txt");
layout: default#
Common Overloading Mistakes#
โ Overloading Pitfalls
// WRONG: Ambiguous varargs public void process(String… args) { } public void process(Object… args) { } // Ambiguous!
// WRONG: Too similar parameter lists public void method(int a, double b) { } public void method(double a, int b) { } // Confusing!
</div>
<div>
```java
// WRONG: Inconsistent behavior
public void save(String file) {
// Saves as text
}
public void save(String file, boolean binary) {
// Completely different behavior!
}
// WRONG: Poor parameter ordering
public void transfer(String from, double amount, String to) { }
public void transfer(double amount, String from, String to) { }
// Inconsistent parameter order is confusing
โ Corrected Approaches
// CORRECT: Specific method names public void processStrings(String… args) { } public void processObjects(Object… args) { }
// CORRECT: Clear parameter differences public void convert(int value, String format) { } public void convert(String value, int precision) { }
</div>
<div>
```java
// CORRECT: Consistent behavior
public void saveAsText(String file, String content) { }
public void saveAsBinary(String file, byte[] data) { }
// CORRECT: Consistent parameter ordering
public void transfer(String from, String to, double amount) { }
public void transferWithFee(String from, String to,
double amount, double fee) { }
// Consistent ordering: from, to, amount
layout: center class: text-center#
Summary#
๐ What We Learned
- โข Method overloading fundamentals and rules
- โข Constructor overloading and chaining
- โข Overloading resolution mechanisms
- โข Advanced overloading with generics and collections
- โข Ambiguity problems and solutions
- โข Best practices for API design
๐ฏ Key Takeaways
- โข Overloading provides flexible, user-friendly APIs
- โข Same logical operation with different inputs
- โข Proper design prevents ambiguity and confusion
- โข Constructor chaining reduces code duplication
- โข Understanding resolution rules prevents surprises
- โข Well-designed overloads improve code maintainability

