CodeGym /Kursy /JAVA 25 SELF /Przeciążanie konstruktorów

Przeciążanie konstruktorów

JAVA 25 SELF
Poziom 14 , Lekcja 4
Dostępny

1. Składnia przeciążania konstruktorów

W życiu rzadko bywa tak, że wszystkie obiekty tworzy się jednakowo. Wyobraź sobie klasę Person (Człowiek). Czasem chcemy utworzyć osobę, znając tylko jej imię. Czasem — imię i wiek. A czasem — nie wiemy nic i niech wszystko będzie domyślne. Byłoby dziwne zmuszać użytkownika klasy do zawsze podawania wszystkich parametrów, nawet jeśli nie są mu potrzebne.

Przeciążanie konstruktorów to sposób, by dać użytkownikowi klasy wybór: które parametry chce podać przy tworzeniu obiektu, a które pozostawić domyślne. Dzięki temu klasa jest elastyczna i wygodna w użyciu.

Analogia:
W biurze konstrukcyjnym buduje się samochody. Ktoś zamawia wersję podstawową (bez klimatyzacji, tylko kierownica i koła), ktoś inny chce „lux” (z podgrzewanymi siedzeniami, Wi‑Fi i żeby samochód jeździł sam). Ale samochód to wciąż ta sama klasa — po prostu różne sposoby jego złożenia!

Przeciążanie oznacza, że w jednej klasie zadeklarowano kilka konstruktorów, lecz z różnymi parametrami (inna liczba, typ lub kolejność parametrów). Wszystkie nazywają się tak samo jak klasa i nie mają typu zwracanego.

Przykład: klasa z przeciążonymi konstruktorami

public class Person {
    String name;
    int age;

    // Konstruktor bez parametrów (domyślny)
    public Person() {
        this.name = "Nieznane";
        this.age = 0;
    }

    // Konstruktor z jednym parametrem
    public Person(String name) {
        this.name = name;
        this.age = 0;
    }

    // Konstruktor z dwoma parametrami
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

Teraz możemy tworzyć obiekty na różne sposoby:

Person p1 = new Person();                // imię = "Nieznane", wiek = 0
Person p2 = new Person("John");         // imię = "John", wiek = 0
Person p3 = new Person("Bob", 25);     // imię = "Bob", wiek = 25

Jak Java rozumie, którego konstruktora użyć?

Java patrzy na liczbę i typy argumentów przekazywanych po new. Jeśli piszesz new Person("John"), kompilator szuka konstruktora z jednym parametrem typu String. Jeśli piszesz new Person("Bob", 25), potrzebny jest konstruktor z parametrami String i int.

2. Wywołanie jednego konstruktora z innego: this(...)

Czasem przy przeciążaniu konstruktorów część kodu się powtarza. Na przykład chcesz, aby wszystkie konstruktory zawsze ustawiał y imię, a wiek, jeśli nie podany, wynosił zero. Aby nie kopiować tej samej logiki w każdym konstruktorze, można wywołać jeden konstruktor z innego za pomocą słowa kluczowego this(...).

Przykład

public class Person {
    String name;
    int age;

    // Konstruktor z dwoma parametrami
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Konstruktor z jednym parametrem wywołuje inny konstruktor
    public Person(String name) {
        this(name, 0); // wywołuje Person(String name, int age)
    }

    // Konstruktor bez parametrów wywołuje inny konstruktor
    public Person() {
        this("Nieznane", 0);
    }
}
public Person(String name) {
    this(name, 0); // wywołuje Person(String, int)
}
Wywołanie jednego konstruktora z innego przez this(...)

Ważna zasada: Wywołanie innego konstruktora przez this(...) musi być pierwszą linią konstruktora!

Po co to jest potrzebne?

  • Zmniejsza duplikację kodu.
  • Jeśli zdecydujesz się zmienić logikę inicjalizacji (np. wartość domyślną), trzeba będzie to zrobić tylko w jednym miejscu.
  • Kod staje się czystszy i łatwiejszy w utrzymaniu.

3. Przykłady praktyczne: przeciążanie w rzeczywistej klasie

Załóżmy, że mamy klasę Account — konto bankowe. Czasem znamy tylko nazwę właściciela, czasem chcemy od razu podać saldo początkowe, a czasem także walutę.

Przykład klasy z przeciążonymi konstruktorami

public class Account {
    String owner;
    double balance;
    String currency;

    // Konstruktor z trzema parametrami
    public Account(String owner, double balance, String currency) {
        this.owner = owner;
        this.balance = balance;
        this.currency = currency;
    }

    // Konstruktor z dwoma parametrami (waluta domyślna — "EUR")
    public Account(String owner, double balance) {
        this(owner, balance, "EUR");
    }

    // Konstruktor z jednym parametrem (saldo = 0, waluta = "EUR")
    public Account(String owner) {
        this(owner, 0.0, "EUR");
    }

    // Konstruktor bez parametrów (właściciel — "Nieznane", saldo = 0, waluta = "EUR")
    public Account() {
        this("Nieznane", 0.0, "EUR");
    }

    public void printInfo() {
        System.out.println(owner + ": " + balance + " " + currency);
    }
}

Użycie

public class Main {
    public static void main(String[] args) {
        Account acc1 = new Account("John", 1000, "USD");
        Account acc2 = new Account("Mary", 500);
        Account acc3 = new Account("Peter");
        Account acc4 = new Account();

        acc1.printInfo(); // John: 1000.0 USD
        acc2.printInfo(); // Mary: 500.0 EUR
        acc3.printInfo(); // Peter: 0.0 EUR
        acc4.printInfo(); // Nieznane: 0.0 EUR
    }
}

Dlaczego to wygodne?

  • Można tworzyć obiekty z różnym poziomem szczegółowości.
  • Nie trzeba za każdym razem podawać wszystkich parametrów (zwłaszcza jeśli często są takie same).
  • Łatwo rozszerzać klasę: jeśli pojawi się nowy parametr, można dodać kolejny konstruktor.

4. Typowe błędy przy przeciążaniu konstruktorów

Błąd nr 1: Pomyłka w typach i kolejności parametrów.
Jeśli masz dwa konstruktory — Person(String name, int age) i Person(int age, String name) — kompilator je rozróżni, ale dla użytkownika klasy może to być bardzo mylące. Lepiej unikać takich sytuacji.

Błąd nr 2: Brak konstruktora domyślnego.
Jeśli zadeklarujesz tylko konstruktory z parametrami, a potem spróbujesz utworzyć obiekt bez parametrów — dostaniesz błąd kompilacji. Zawsze dodawaj konstruktor bez parametrów, jeśli jest potrzebny.

Błąd nr 3: Próba wywołania innego konstruktora nie w pierwszej linii.
Wywołanie this(...) zawsze musi być pierwszą linią konstruktora. Jeśli napiszesz coś przed tym — będzie błąd kompilacji.

Błąd nr 4: Zapętlenie wywołań konstruktorów.
Jeśli konstruktor wywołuje sam siebie (bezpośrednio lub przez łańcuch), doprowadzi to do błędu kompilacji z powodu nieskończonej rekursji.

Błąd nr 5: Niezinicjalizowane pola.
Jeśli zapomnisz zainicjalizować jakieś pole w konstruktorze (lub w łańcuchu wywołań), obiekt może znaleźć się w niepoprawnym stanie. Upewnij się, że wszystkie pola mają sensowne wartości po utworzeniu obiektu.

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