Archiwa tagu: static

„Stałe” w Javie:
final, static, i niemodyfikowalność

Wpis z dedykacją dla D, który postanowił nauczyć się nowego języka programowania i, chcąc utworzyć stałą (constant – słowo kluczowe niedostępne w Javie),  zapytał mnie o relację pomiędzy terminami: final, static i obiekt niemodyfikowalny (immutable).

final

Słowo final zmienia znaczenie w zależności od kontekstu, ale zawsze oznacza, że coś jest „ostateczne” i po utworzeniu nie da się już tego zmienić.

Klasa final

Jeśli klasę oznaczymy słowem final, nie będzie można jej rozszerzać, czyli tworzyć jej podklas. Zatem będzie to „ostateczna” wersja danej klasy.

Metoda final

Z metodami jest podobnie jak z klasami. Jeśli słowem final oznaczymy metodę, nie będzie można jej przesłaniać w klasach pochodnych – czyli mamy do czynienia z „ostateczną” wersją danej metody.

Zmienna final

Jeśli jako final opiszemy zwykłą zmienną w środku kodu, wartość (a dokładniej: wartość w przypadku typów prostych lub referencję w przypadku obiektów) będzie można jej przypisać maksymalnie raz. Na przykład:

Pole klasy final

Jeśli słowem final oznaczymy pole klasy, będzie to oznaczało, że wartość (lub referencję, jak powyżej) można mu nadać tylko raz – będzie ona ostateczna. Wartość tę można przypisać na dwa sposoby – albo „w miejscu”, przy definiowaniu klasy, albo w konstruktorze. Nie da się tego zrobić później. Jeśli pole final nie otrzyma wartości w żadnym z tych dwóch miejsc, kompilator zaprostesuje.

Za przykład niech posłuży fragment definicji klasy String z Javy 8:

Parametr final

Jeśli jako final oznaczymy parametr metody,  zablokujemy możliwość zmiany jego wartości. I tak nie powinniśmy tego robić – to jedna z dobrych praktyk. Użycie słowa final oficjalnie nas do niej zobowiązuje.

static

Słowo kluczowe static nie musi być łączone ze stałymi. Chcę jednak rozebrać je na czynniki pierwsze, ponieważ często spotyka się je razem z final w następujących konstrukcjach (ta akurat znów klasy String):

Słowo „statyczny”, jako przeciwieństwo „dynamiczny” można w uproszczeniu rozumieć w ten sposób, że opisany nim byt do życia nie potrzebuje obiektów (tworzonych dynamicznie).

Pole klasy static

Jeśli pole klasy zostanie oznaczone jako static (jak w przykładzie bezpośrednio powyżej), oznacza to, że jest współdzielone przez wszystkie obiekty danej klasy. Można uzyskać do niego dostęp odwołując się do nazwy obiektu lub do nazwy klasy – efekt będzie ten sam. Pamięć jest przydzielana tylko raz, podczas ładowania klasy.

W ten sposób można definiować ważne stałe albo różnego rodzaju liczniki (np. liczba egzemplarzy danej klasy – wówczas w konstruktorze zwiększamy wartość tego pola o 1).

Metoda static

Metoda static jest współdzielona przez wszystkie obiekty, można ją także wywołać odnosząc się bezpośrednio do nazwy klasy, bez potrzeby tworzenia obiektu.

Metoda ta ma dostęp jedynie do statycznych atrybutów (pól i metod) danej klasy. Co za tym idzie, nie może też używać słów this ani super.

W ten sposób często definiuje się różne metody pomocnicze – na przykład większość metod w klasy Math.

Najbardziej znana metoda statyczna to oczywiście main.

Blok kodu static

Wewnątrz definicji klasy można umieścić statyczny blok kodu, w którym, na przykład, zainicjalizujemy jakieś statyczne pola. Zostanie on wywołany w trakcie ładowania klasy.

Mimo że bloki statyczne rzadko pojawiają się w kodzie, kolejność ich wykonywania to konik wielu profesorów informatyki na egzaminach z programowania obiektowego.

Zagnieżdżona klasa static

Jeśli klasę zagnieżdżoną opiszemy słowem static, będzie ona mogła odwoływać się jedynie do statycznych atrybutów swojej klasy zewnętrznej.

Obiekty niemodyfikowalne

Obiekt niemodyfikowalny (immutable) to taki obiekt, w którym po użyciu konstruktora nie można już dokonywać żadnych zmian.

Przekazując taki obiekt, mamy pewność, że nie zostanie on zmieniony przez żaden kod – ani nasz, ani znajdujący się pod kontrolą kogoś innego. Obiekty niemodyfikowalne są wybawieniem podczas pracy z wątkami, ponieważ nie trzeba synchronizować ich stanu.

Niemutowalne są na przykład obiekty klasy Integer.

Oto fragment definicji tej klasy:

Wartość value (czyli liczbę typu prostego int opakowywaną przez klasę Integer) można ustawić jedynie przy użyciu któregoś z konstruktorów. Nie da się jej zmienić później. Klasa Integer ma szereg metod pozwalających uzyskać dostęp do tej wartości, ale sama wartość wewnątrz obiektu tej klasy nigdy  nie zostanie zmieniona. Nie jest do tego potrzebne słowo final (które zostało tu użyte) – wystarczyłaby sama enkapsulacja. final daje nam jednak niezbitą pewność, że po inicjalizacji nikt już tej wartości nie zmieni.

Co ma zrobić osoba, która potrzebuje innej liczby Integer? Będzie musiała po prostu utworzyć nowy obiekt.

Nie istnieje słowo kluczowe immutable. Obiekty niemodyfikowalne tworzy programista, korzystając z mechanizmu enkapsulacji oraz słowafinal.

Wszystko razem, czyli static final String

Rozważmy definicję poniższych pól:

Wszystkie trzy pola są opisane jako static final, czyli są to stałe współdzielone przez wszystkie obiekty danej klasy.

W pierwszym przypadku mamy stałą typu prostego. Sprawa jest oczywista – nie wolno nam zmienić jej wartości. Nie możemy przypisać pod ERROR_CODE żadnej innej wartości.

W drugim przypadku mamy obiekt. Jest to, dodatkowo, obiekt niemodyfikowalny (immutable). Uczący się Javy kolega zapytał ostatnio, po co dodawać final, skoro obiekt jest niemodyfikowalny. Po co? – żeby nie można było zmienić referencji. Gdyby nie słowo final, możliwa byłaby następująca operacja:

Obiekt String z wartością „Ouch” jest co prawda niemodyfikowalny (nie można zmienić przechowywanej w nim wartości), ale nic nie stałoby na przeszkodzie, żeby podmienić obiekty przypisane do zmiennej RESPONSE. Użycie final nas przed tym chroni.

W ostatnim przypadku mamy zwykły (modyfikowalny) obiekt ze zmiennym stanem, przypisany do pola oznaczonego jako final. Co to oznacza? Że co prawda, dzięki final, nie możemy zmienić referencji (to będzie nadal tem sam obiekt) ale ciągle możemy nieźle namieszać w jego wnętrzu przy użyciu API:

Jasne?

Odpowiedne korzystanie z javowych „stałych”, wartości statycznych i obiektów niemodyfikowalnych pozwala na oszczędzenie miejsca w pamięci i stworzenie czytelnego, łatwego w utrzymaniu kodu.