CodeGym /Kursy /JAVA 25 SELF /Błędy związane z modyfikatorami dostępu

Błędy związane z modyfikatorami dostępu

JAVA 25 SELF
Poziom 23 , Lekcja 2
Dostępny

1. Wprowadzenie

W Javie modyfikatory dostępu — to jak system zamków w domu. Określają, kto i skąd może „wejść” do ciebie do pokoju (albo do pola/metody twojej klasy). Jeśli wszystkie drzwi są otwarte — każdy może przyjść i coś zmienić. Jeśli wszystko jest zamknięte — nikt niczego nie zepsuje, ale i ty sam w pewnym momencie możesz zostać zamknięty na klucz.

Przypomnijmy: w Javie są cztery podstawowe poziomy dostępu:

Modyfikator Dostępny wewnątrz klasy Dostępny w pakiecie Dostępny w podklasach Dostępny w innych pakietach
private
(package)
protected
(przez dziedziczenie)
public

(package) — to wtedy, gdy modyfikator nie jest podany jawnie. Taki członek klasy jest widoczny tylko w obrębie jednego pakietu.

2. Typowe błędy związane z modyfikatorami dostępu

Błąd 1: Pola i metody z domyślnym modyfikatorem (package-private)

Najczęstszy błąd początkujących — zapomnieć wskazać modyfikator dostępu. W rezultacie pole lub metoda stają się dostępne w całym pakiecie, choć nie było to planowane. To może doprowadzić do tego, że inna klasa (w tym samym pakiecie, ale niezwiązana z twoją) będzie mogła zmienić wewnętrzny stan twojego obiektu.

// Błąd: pole name nie jest chronione!
class User {
    String name; // package-private!
}

W rezultacie dowolna klasa z tego pakietu może napisać:

User user = new User();
user.name = "John"; // bez ograniczeń!

Błąd 2: Naruszenie enkapsulacji — publiczne pola (public)

Drugi co do popularności błąd — deklarowanie pól klasy jako public. To wygodne, gdy dopiero się uczysz lub piszesz krótki przykład, ale w prawdziwych projektach to niemal zawsze kiepski pomysł. Tracisz kontrolę nad tym, kto i jak zmienia twoje dane.

public class Account {
    public double balance; // NIEBEZPIECZNE!
}

Teraz dowolny kod może zrobić:

Account acc = new Account();
acc.balance = -1000000; // I kto jest teraz winny?

Błąd 3: Brak getterów i setterów

Czasem programista robi pola private, ale zapomina dodać metody do zarządzania nimi. W rezultacie nie da się pobrać ani zmienić wartości nawet tam, gdzie byłoby to zasadne.

public class Product {
    private String name;
    // Brak ani getName(), ani setName()
}

Błąd 4: Próba dostępu do elementów private z innej klasy

Jeśli zadeklarowałeś pole lub metodę jako private, to nie można się do nich odwołać z innej klasy, nawet jeśli znajduje się w tym samym pakiecie. Początkujących często dziwi, dlaczego „nie widzi” pola.

public class User {
    private String password;
}

public class UserService {
    public void resetPassword(User user) {
        // user.password = "123"; // Błąd kompilacji!
    }
}

Błąd 5: Błędy związane z protected

Wielu uważa, że protected oznacza „widoczne wszędzie, gdzie jest dziedziczenie”. Ale w Javie dostęp do elementów protected poza pakietem jest możliwy tylko przez dziedziczenie i wyłącznie dla podklasy. To subtelność, którą łatwo przeoczyć.

package animals;

public class Animal {
    protected void sleep() {}
}

package zoo;
import animals.Animal;

public class Dog extends Animal {
    public void test() {
        sleep(); // OK — podklasa
    }
}

public class NotADog {
    public void test() {
        Animal a = new Animal();
        // a.sleep(); // Błąd: to nie podklasa!
    }
}

3. Jak robić to poprawnie: dobre praktyki

Reguła 1: Domyślnie ustawiaj pola jako private

To główna zasada enkapsulacji. Pola powinny być ukryte przed wszystkimi poza samą klasą. Jeśli trzeba dać dostęp — używaj getterów/setterów.

public class Book {
    private String title;
    private int pages;

    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
}

Reguła 2: Ujawniaj tylko niezbędne metody

Jeśli metoda ma być dostępna z zewnątrz — oznacz ją jako public. Jeśli jest potrzebna tylko w obrębie pakietu — pozostaw package-private. Jeśli metoda jest przeznaczona tylko dla dziedziczących — użyj protected.

Reguła 3: Minimalizuj zakres widoczności

Im mniejszy zakres widoczności, tym mniejsze ryzyko przypadkowych błędów i „nieoczekiwanych gości”. Nie rób metod i pól public, jeśli nie ma takiej potrzeby.

Reguła 4: Używaj getterów i setterów do kontroli dostępu

To pozwala dodać dodatkową logikę podczas odczytu/zapisu pola, np. walidację.

public class Account {
    private double balance;

    public void setBalance(double balance) {
        if (balance < 0) {
            throw new IllegalArgumentException("Saldo nie może być ujemne!");
        }
        this.balance = balance;
    }

    public double getBalance() {
        return balance;
    }
}

Reguła 5: Nie ujawniaj wewnętrznej implementacji

Jeśli masz tablicę lub listę jako pole, nie zwracaj jej bezpośrednio przez getter — zwracaj kopię albo udostępniaj tylko potrzebne metody.

public class Team {
    private List<String> members = new ArrayList<>();

    // Poprawnie:
    public List<String> getMembers() {
        return new ArrayList<>(members); // zwracamy kopię
    }
}

4. Przykłady w praktyce

Załóżmy, że mamy klasę LibraryUser, która opisuje użytkownika biblioteki.

Przykład niepoprawnej implementacji

public class LibraryUser {
    public String name;
    public int borrowedBooks;
}

W takiej postaci dowolny kod może zrobić z obiektem cokolwiek:

LibraryUser user = new LibraryUser();
user.name = null;
user.borrowedBooks = -10; // Logika? Jaka logika?

Przykład poprawnej implementacji z enkapsulacją

public class LibraryUser {
    private String name;
    private int borrowedBooks;

    public LibraryUser(String name) {
        this.name = name;
        this.borrowedBooks = 0;
    }

    public String getName() {
        return name;
    }

    public int getBorrowedBooks() {
        return borrowedBooks;
    }

    public void borrowBook() {
        borrowedBooks++;
    }

    public void returnBook() {
        if (borrowedBooks > 0) {
            borrowedBooks--;
        }
    }
}

Teraz kod zewnętrzny nie może bezpośrednio zmienić liczby wypożyczonych książek ani imienia użytkownika. Wszystko jest kontrolowane wyłącznie przez metody klasy.

5. Szczegóły i niuanse implementacji

Czasem wydaje się, że łatwiej zrobić pole public, niż pisać mnóstwo getterów i setterów. Ale to pułapka! Otwarte pole — to jak otwarte drzwi do mieszkania: owszem, wygodnie, ale mało bezpiecznie.

Jeszcze jedna subtelność — nie zawsze trzeba tworzyć gettery i settery dla wszystkich pól. Jeśli wartość pola nie powinna się zmieniać po utworzeniu obiektu, zrób tylko getter, a pole oznacz jako final:

public class Passport {
    private final String number;

    public Passport(String number) {
        this.number = number;
    }

    public String getNumber() {
        return number;
    }
}

Pamiętaj także: jeśli klasa jest zadeklarowana jako public, nazwa pliku musi odpowiadać nazwie klasy! To nie do końca o modyfikatorach dostępu, ale to bardzo częsty błąd początkujących.

6. Typowe błędy podczas pracy z modyfikatorami dostępu

Błąd nr 1: zapomniano wskazać modyfikator dostępu przy polu lub metodzie. W rezultacie pole lub metoda stają się dostępne w całym pakiecie, nawet jeśli nie było to zamierzone. Zawsze jawnie określaj modyfikator, nawet jeśli IDE nie narzeka.

Błąd nr 2: wszystkie pola zadeklarowane jako public. To zabija enkapsulację, czyni twój kod podatnym i nieprzewidywalnym. Nawyk z przykładów „dla prostoty” nie powinien trafiać do kodu produkcyjnego.

Błąd nr 3: próba odwołania się do pola private z innej klasy. Java na to nie pozwoli — kompilator cię ochroni, ale jeśli zechcesz to „obejść” refleksją — zastanów się, dlaczego w ogóle pojawiła się taka potrzeba.

Błąd nr 4: oczekiwanie, że elementy protected będą dostępne wszędzie, gdzie jest dziedziczenie. W rzeczywistości poza pakietem można się do nich odwołać tylko z podklasy i tylko przez this lub przez obiekt podklasy.

Błąd nr 5: zwracanie wewnętrznej kolekcji przez getter. Jeśli zwrócisz referencję do wewnętrznej tablicy lub listy, kod zewnętrzny będzie mógł ją zmodyfikować, naruszając inwarianty klasy.

Błąd nr 6: brak kontroli przy ustawianiu wartości przez setter. Jeśli nie zweryfikujesz wartości wejściowej, możesz uzyskać niepoprawny stan obiektu (np. ujemne saldo).

Błąd nr 7: zbyt szeroki zakres widoczności metod. Czasem metody oznacza się jako public, choć są potrzebne tylko w obrębie pakietu lub klasy. To otwiera zbędne API i utrudnia utrzymanie.

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