programowanieProgramista Java

Czym są obiekty niemutowalne w Javie, jaka jest ich wartość i jak poprawnie zaimplementować własną klasę niemutowalną?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Obiekty niemutowalne — to obiekty, których stan nie może być zmieniony po utworzeniu. Ich główne cechy:

  • Wszystkie pola są final;
  • Nie zawierają setterów;
  • Do obiektów wewnętrznych (np. kolekcji) nie można uzyskać mutowalnego odniesienia.

Zalety obiektów niemutowalnych:

  • Bezpieczne w dostępie wielowątkowym (thread-safe);
  • Łatwe do cachowania i ponownego użycia jako klucze w kolekcjach;
  • Ułatwiają debugowanie i testowanie;
  • Mniej błędów z powodu nieoczekiwanych zmian stanu.

Przykład implementacji klasy niemutowalnej:

public final class Person { private final String name; private final int age; private final List<String> phones; public Person(String name, int age, List<String> phones) { this.name = name; this.age = age; // Ochrona przed mutacją przekazanej listy this.phones = Collections.unmodifiableList(new ArrayList<>(phones)); } public String getName() { return name; } public int getAge() { return age; } public List<String> getPhones() { return phones; } // Zwracamy listę do odczytu }

Pytanie z haczykiem.

Dlaczego String w Javie jest niemutowalny i co by się stało, gdyby tak nie było? Wiele osób odpowiada "dla bezpieczeństwa", ale co to oznacza w praktyce?

Odpowiedź:

String jest używany w wielu miejscach: jako klucze w kolekcjach, w logice bezpieczeństwa (np. hasła). Gdyby można było zmieniać stringa przez jedno odniesienie, wpłynęłoby to na wszystkie inne odniesienia do tego samego obiektu, co uniemożliwiłoby prawidłowe działanie kolekcji (np. HashMap — podczas obliczania hashCode) i mogłoby prowadzić do luk w bezpieczeństwie.

Przykłady rzeczywistych błędów z powodu braku wiedzy na ten temat.


Historia

W dużym projekcie bankowym przekazywano wewnętrzne kolekcje (lista transakcji) zwykłym getterem. W rezultacie lista mogła być zmieniana z zewnątrz, co naruszało invarianty (np. dodanie transakcji z niepoprawną datą). Doprowadziło to do utraty danych, dopóki nie zaczęli zwracać Collections.unmodifiableList.

Historia

W klasie konfiguracji przechowywane były niechronione pola-obiekty (Date, List). W jednym wątku konfiguracja została zmieniona, a w drugim uzyskano przestarzałe lub niespójne dane, przez co błędnie działał algorytm biznesowy.

Historia

W systemie logowania hasła były przechowywane w zmiennym obiekcie. Z powodu niebezpiecznego dostępu nagle "wyciekło" hasło innego użytkownika, ponieważ ten sam obiekt był używany przez wiele wątków.