CodeGym /Kursy /JAVA 25 SELF /Gettery i settery: składnia, najlepsze praktyki

Gettery i settery: składnia, najlepsze praktyki

JAVA 25 SELF
Poziom 15 , Lekcja 2
Dostępny

1. Czym są gettery i settery

Jeśli wyobrazić sobie obiekt jako sejf, to jego prywatne pola — to zawartość sejfu, a gettery i settery — klucze do poszczególnych przegródek. Gettery pozwalają sprawdzić, co jest w środku, a settery — ostrożnie włożyć tam coś nowego (ale tylko jeśli nie wkładasz tam, na przykład, jeża zamiast dokumentów).

Getter

Getter — to publiczna metoda (public), która zwraca wartość prywatnego pola (private). Zwykle jego nazwa zaczyna się od get + nazwa pola z wielkiej litery.

public class Person {
    private String name; // Pole prywatne

    // Getter dla pola name
    public String getName() {
        return name;
    }
}

Setter

Setter — to publiczna metoda (public), która pozwala zmienić wartość prywatnego pola. Jego nazwa zaczyna się od set + nazwa pola z wielkiej litery.

public class Person {
    private String name;

    // Setter dla pola name
    public void setName(String name) {
        this.name = name;
    }
}

Dla pól typu boolean

Dla pól typu boolean przyjęto używać prefiksu is w getterach:

private boolean active;

public boolean isActive() {
    return active;
}

public void setActive(boolean active) {
    this.active = active;
}

2. Składnia i konwencje nazewnicze

Java to język rygorystyczny, ale nie maruda. Istnieją utrwalone konwencje, które sprawiają, że twój kod jest zrozumiały dla innych programistów (i dla ciebie samego za miesiąc).

  • Getter: public Type getFieldName()
  • Setter: public void setFieldName(Type value)
  • Getter dla boolean: public boolean isFieldName()

Nazwa pola w metodzie pisana jest wielką literą: jeśli pole to age, to metody będą getAge() i setAge().

Ta konwencja wspiera styl JavaBeans, dzięki któremu IDE, biblioteki i frameworki mogą automatycznie odnajdywać twoje gettery i settery. Na przykład, jeśli używasz Springa lub JavaFX, te metody będą „magicznie” wywoływane, kiedy będzie to potrzebne.

3. Przykłady kodu

Weźmy projekt edukacyjny — prostą aplikację „kontakty” (odpowiednik książki telefonicznej) — i dodajmy do niej poprawne gettery i settery.

Przykład: klasa Contact z prywatnymi polami i getterami/setterami

public class Contact {
    private String name;
    private String phone;
    private int age;
    private boolean favorite;

    // Gettery
    public String getName() {
        return name;
    }

    public String getPhone() {
        return phone;
    }

    public int getAge() {
        return age;
    }

    public boolean isFavorite() {
        return favorite;
    }

    // Settery
    public void setName(String name) {
        this.name = name;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public void setAge(int age) {
        // Przykład walidacji: wiek nie może być ujemny
        if (age < 0) {
            System.out.println("Wiek nie może być ujemny!");
            return;
        }
        this.age = age;
    }

    public void setFavorite(boolean favorite) {
        this.favorite = favorite;
    }
}

Użycie w aplikacji

Contact friend = new Contact();
friend.setName("Ivan Ivanov");
friend.setPhone("+1-999-123-45-67");
friend.setAge(25);
friend.setFavorite(true);

System.out.println("Imię: " + friend.getName());
System.out.println("Telefon: " + friend.getPhone());
System.out.println("Wiek: " + friend.getAge());
System.out.println("Ulubiony: " + (friend.isFavorite() ? "Tak" : "Nie"));

Przykład walidacji w setterze

Zwróć uwagę, że w setterze setAge dodaliśmy prostą weryfikację: jeśli wiek jest ujemny, nie zmieniamy pola i wypisujemy ostrzeżenie. To prosty sposób na ochronę obiektu przed niepoprawnymi danymi.

4. Najlepsze praktyki: jak robić to poprawnie

Nie wszystkie pola powinny mieć publiczne settery

Czasem pole powinno być tylko do odczytu — na przykład unikalny identyfikator ustawiany przy tworzeniu obiektu i już niezmieniany. W takim przypadku settera po prostu się nie pisze:

public class Contact {
    private final int id; // final — nie można zmienić po inicjalizacji

    public Contact(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    // Brak setId!
}

Używaj getterów/setterów do kontroli dostępu i walidacji

public void setName(String name) {
    if (name == null || name.trim().isEmpty()) {
        System.out.println("Imię nie może być puste!");
        return;
    }
    this.name = name;
}

Nie ujawniaj bezpośrednio wewnętrznych obiektów modyfikowalnych

Jeśli masz pole — na przykład listę telefonów:

private String[] phones;

Nie należy zwracać go bezpośrednio przez getter:

public String[] getPhones() {
    return phones; // Źle!
}

Taki kod pozwala zewnętrznemu kodowi zmieniać listę jak tylko chce — naruszając enkapsulację!

Lepiej: zwracać kopię tablicy:

public String[] getPhones() {
    return Arrays.copyOf(phones, phones.length); // Zwracamy kopię
}

Albo po prostu sklonować:

public String[] getPhones() {
    return phones.clone(); 
}

Rób gettery i settery zrozumiałe i proste

  • Nie umieszczaj skomplikowanej logiki biznesowej w getterach/setterach — ich zadanie jest proste: kontrola dostępu i, w razie potrzeby, walidacja.
  • Jeśli pole nie powinno się zmieniać — w ogóle nie twórz settera.
  • Jeśli pole nie powinno być dostępne z zewnątrz — nie twórz gettera.

5. Automatyczne generowanie getterów/setterów w IDE

Pisanie akcesorów ręcznie to średnia przyjemność, zwłaszcza gdy klasa ma dziesięć pól. Na szczęście nowoczesne IDE (np. IntelliJ IDEA, Eclipse, VS Code z wtyczkami) potrafią generować je automatycznie.

W IntelliJ IDEA

  1. Otwórz klasę, umieść kursor wewnątrz ciała klasy.
  2. Naciśnij Alt + Insert (albo Code -> Generate...).
  3. Wybierz Getter and Setter.
  4. Zaznacz wymagane pola i kliknij OK.

Voilà! Twoje gettery i settery pojawią się jak za dotknięciem czarodziejskiej różdżki.

W Eclipse

  1. Otwórz klasę.
  2. Prawy przycisk myszy — SourceGenerate Getters and Setters...
  3. Wybierz pola i kliknij OK.

W VS Code (z Java Extension Pack)

  1. Otwórz plik klasy.
  2. W palecie poleceń (Ctrl+Shift+P) wpisz Generate getters and setters.
  3. Postępuj zgodnie z podpowiedziami.

6. Rozwój twojej aplikacji: enkapsulacja w praktyce

W poprzednich lekcjach budowałeś prostą aplikację do przechowywania kontaktów. Teraz możemy ją ulepszyć, ustawiając pola jako prywatne i zapewniając dostęp wyłącznie przez gettery/settery.

Było (zły przykład):

public class Contact {
    public String name;
    public String phone;
    public int age;
}

Problem: dowolny kod może zrobić tak:

Contact c = new Contact();
c.age = -1000; // Teraz mamy wampira w książce telefonicznej!

Po zmianie (dobry przykład):

public class Contact {
    private String name;
    private String phone;
    private int age;

    public void setAge(int age) {
        if (age < 0) {
            System.out.println("Wiek nie może być ujemny!");
            return;
        }
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    // Pozostałe gettery/settery...
}

Teraz nie da się przypadkowo (ani celowo) zepsuć obiektu z zewnątrz.

7. Gettery/settery dla właściwości wyliczanych i niezmiennych

Czasem wartość nie jest przechowywana w polu, tylko obliczana w locie:

public class Rectangle {
    private int width;
    private int height;

    public int getArea() {
        return width * height;
    }
}

Setter dla pola powierzchni nie jest potrzebny — nie można jej ustawić bezpośrednio, można tylko zmienić szerokość lub wysokość.

8. Gettery i settery: typowe błędy

Błąd nr 1: Getter/setter narusza enkapsulację.
Jeśli getter zwraca referencję do wewnętrznego obiektu modyfikowalnego (np. listy), kod zewnętrzny może go zmienić, omijając wszelkie weryfikacje. To podważa ideę enkapsulacji.

Błąd nr 2: Setter nie waliduje danych.
Jeśli setter po prostu przypisuje wartość, nie sprawdzając jej, można uzyskać niepoprawny stan obiektu (np. ujemny wiek lub puste imię).

Błąd nr 3: Automatyczne generowanie setterów dla wszystkich pól.
IDE może wygenerować settery dla wszystkich pól, ale to nie zawsze jest właściwe! Na przykład dla identyfikatora (id) setter nie jest potrzebny.

Błąd nr 4: Złożona logika w getterach/setterach.
Gettery i settery powinny być proste. Jeśli pojawia się w nich złożona logika biznesowa, warto wydzielić ją do oddzielnych metod.

Błąd nr 5: Naruszanie konwencji nazewniczych.
Jeśli nazwiesz getter fetchName() zamiast getName(), niektóre frameworki i biblioteki mogą nie rozpoznać go poprawnie.

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