CodeGym /Kursy /JAVA 25 SELF /Przeciążanie metod (overloading)

Przeciążanie metod (overloading)

JAVA 25 SELF
Poziom 18 , Lekcja 1
Dostępny

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.

Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION