1. Przeciążanie metod
Przeciążanie metod (method overloading) — to możliwość zadeklarowania w jednej klasie kilku metod o tej samej nazwie, ale różnych listach parametrów (typach, liczbie lub kolejności argumentów). Gdy wywołujesz taką metodę, kompilator sam decyduje, której wersji użyć, w zależności od przekazanych argumentów.
Analogiczna sytuacja z życia
Wyobraź sobie, że dzwonisz na infolinię banku. Możesz zadzwonić pod różne numery — dla klientów indywidualnych, dla firm, dla klientów VIP. Numery są różne, ale cel jest jeden — uzyskać pomoc. W programowaniu przeciążanie metod jest jak jeden numer wsparcia, który automatycznie łączy cię z odpowiednim operatorem w zależności od tego, jaką masz sprawę (czyli jakie parametry przekazałeś).
Składnia przeciążania w Javie
W Javie przeciążanie metod realizuje się bardzo prosto: deklarujesz kilka metod o tej samej nazwie, ale z różnymi parametrami.
Ważna zasada:
Przeciążanie różni się wyłącznie listą parametrów (typ, liczba, kolejność).
Przeciążanie wyłącznie po typie zwracanym jest niedozwolone!
Kompilator nie odróżni metod, jeśli mają tę samą nazwę i te same parametry, nawet jeśli typ zwracany jest inny.
Prosty przykład
public class Printer {
// Wydruk liczby całkowitej
void print(int x) {
System.out.println("int: " + x);
}
// Wydruk łańcucha
void print(String s) {
System.out.println("String: " + s);
}
// Wydruk dwóch liczb
void print(int x, int y) {
System.out.println("int, int: " + x + ", " + y);
}
}
Użycie:
Printer printer = new Printer();
printer.print(42); // wywoła print(int x)
printer.print("Hello!"); // wywoła print(String s)
printer.print(5, 10); // wywoła print(int x, int y)
Kompilator sam określa, którą metodę wywołać, na podstawie przekazanych argumentów.
2. Jak kompilator wybiera właściwą metodę
Gdy wywołujesz przeciążoną metodę, kompilator analizuje listę argumentów i szuka najbardziej pasującej wersji.
Kryteria wyboru:
- Zgodność liczby argumentów.
- Zgodność typu każdego argumentu (lub możliwość konwersji typu, np. int → double).
- Jeśli pasuje kilka metod, wybierana jest najbardziej specyficzna.
Przykład:
public class OverloadDemo {
void show(int x) {
System.out.println("show(int): " + x);
}
void show(double x) {
System.out.println("show(double): " + x);
}
public static void main(String[] args) {
OverloadDemo demo = new OverloadDemo();
demo.show(5); // show(int): 5
demo.show(5.5); // show(double): 5.5
}
}
Jeśli wywołasz demo.show(5), kompilator wybierze show(int). Jeśli wywołasz z 5.5 — wybierze show(double).
Konwersje typów
Jeśli nie ma pasującej metody, kompilator spróbuje przekonwertować typy argumentów (np. int → double), ale tylko wtedy, gdy jest to możliwe i nie wprowadza niejednoznaczności.
void print(double x) { /* ... */ }
print(5); // int 5 zostanie przekonwertowane na double 5.0
Przeciążanie tylko po typie zwracanym — nie działa!
Wielu początkujących próbuje zrobić tak:
// Błąd! Taka postać przeciążenia nie jest możliwa
int sum(int x, int y) { return x + y; }
double sum(int x, int y) { return (double) (x + y); }
Kompilator nie zrozumie, którą metodę chcesz wywołać, jeśli po prostu napiszesz sum(2, 3).
Zapamiętaj: przeciążanie metod jest możliwe tylko według parametrów, a nie typu zwracanego!
3. Przeciążanie konstruktorów
Można przeciążać nie tylko zwykłe metody, ale także konstruktory!
public class Person {
String name;
int age;
// Konstruktor tylko z imieniem
public Person(String name) {
this.name = name;
this.age = 0; // domyślnie
}
// Konstruktor z imieniem i wiekiem
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
Użycie:
Person p1 = new Person("Anna");
Person p2 = new Person("Borys", 25);
4. Praktyczne przykłady
Calculator z przeciążaniem
Napiszmy klasę Calculator, która potrafi dodawać liczby różnych typów i w różnej liczbie.
public class Calculator {
// Dodawanie dwóch liczb całkowitych
int add(int a, int b) {
return a + b;
}
// Dodawanie trzech liczb całkowitych
int add(int a, int b, int c) {
return a + b + c;
}
// Dodawanie dwóch double
double add(double a, double b) {
return a + b;
}
}
Użycie:
Calculator calc = new Calculator();
System.out.println(calc.add(2, 3)); // 5
System.out.println(calc.add(1, 2, 3)); // 6
System.out.println(calc.add(2.5, 3.1)); // 5.6
Przeciążanie metod w bibliotece standardowej
Już miałeś do czynienia z przeciążaniem, nawet jeśli tego nie zauważyłeś. Na przykład metoda println w System.out jest przeciążona dla różnych typów:
System.out.println("Hello"); // println(String)
System.out.println(123); // println(int)
System.out.println(3.14); // println(double)
System.out.println(true); // println(boolean)
Otwórz kod źródłowy klasy PrintStream — zobaczysz dziesiątki przeciążonych wersji println.
5. Kiedy używać przeciążania
Przeciążanie metod to potężne narzędzie, ale trzeba używać go z rozwagą. Jest wygodne, gdy:
- Logika metody jest taka sama, ale parametry mogą się różnić.
- Chcesz uczynić API klasy bardziej przyjaznym i elastycznym.
- Trzeba wspierać stare i nowe warianty wywołania metody.
Przykład z życia:
W naszej aplikacji edukacyjnej (np. w menedżerze zadań) można zaimplementować metodę dodawania zadania z różnymi parametrami:
public class TaskManager {
void addTask(String description) { ... }
void addTask(String description, int priority) { ... }
void addTask(String description, int priority, String deadline) { ... }
}
Takie podejście jest nie tylko wygodne dla użytkownika kodu, ale też czyni klasę elastyczną na przyszłe zmiany.
6. Przeciążanie i varargs (zmienna liczba argumentów)
W Javie można zadeklarować metodę ze zmienną liczbą parametrów przy użyciu ... (varargs):
void printAll(String... messages) {
for (String msg : messages) {
System.out.println(msg);
}
}
Teraz można wywoływać:
printAll("Hello");
printAll("One", "Two", "Three");
Można też przeciążać metody z varargs, ale bądź ostrożny: jeśli istnieją dwie metody — jedna z varargs, a druga z ustaloną liczbą parametrów — kompilator najpierw spróbuje znaleźć dokładne dopasowanie.
7. Typowe błędy przy przeciążaniu metod
Błąd nr 1: Zamieszanie z typami argumentów.
Jeśli masz metody void process(int x) i void process(double x), wywołanie process(5) uruchomi pierwszą wersję, a process(5.0) — drugą. Jednak jeśli wywołasz process(5L), kompilator będzie szukał najlepszego dopasowania i może wybrać nieoczywiste przeciążenie (albo zgłosić niejednoznaczność).
Błąd nr 2: Przeciążanie z autokonwersją.
Jeśli masz void foo(Integer x) i void foo(Long x), wywołanie foo(5) spowoduje błąd kompilacji — kompilator nie wie, którą metodę wybrać, ponieważ 5 można przypisać zarówno do Integer, jak i do Long.
Błąd nr 3: Przeciążanie tylko po typie zwracanym.
Jak wspomniano wyżej, metody różniące się wyłącznie typem zwracanym nie mogą być przeciążone.
Błąd nr 4: Przeciążanie a dziedziczenie.
Jeśli w klasie bazowej zadeklarowano metodę, a w klasie pochodnej deklarujesz metodę o tej samej nazwie, ale innej sygnaturze, będzie to przeciążanie, a nie przesłanianie. To często bywa mylone! Więcej o tym — w następnej lekcji.
GO TO FULL VERSION