Oznaczając metodę klasy modyfikatorem
final
, mamy na myśli to, że żadna klasa pochodna nie jest w stanie zastąpić tej metody poprzez zmianę jej wewnętrznej implementacji. Inaczej mówiąc, mówimy o ostatecznej wersji metody. Klasę jako całość można również oznaczyć jako final
.
final class NoExtending {
// …
}
Klasy oznaczonej jako final
nie można dziedziczyć, a wszystkie jej metody pośrednio nabywają tę właściwość final
. Użycie funkcji a final
w deklaracjach klas i metod może zwiększyć poziom bezpieczeństwa kodu. Jeśli klasa jest wyposażona w modyfikator final
, nikt nie może rozszerzyć klasy i prawdopodobnie złamać przy tym jej kontrakt. Jeśli znak final
oznacza metodę, możesz w pełni zaufać jej wewnętrznej implementacji w każdej sytuacji, bez obawy o „fałszowanie”. Właściwe jest użycie final
np. w deklaracji metody wymagającej weryfikacji wprowadzonego przez użytkownika hasła, aby mieć pewność, że zostanie wykonane dokładnie to, co pierwotnie zakładała metoda. Ewentualny atakujący nie będzie w stanie zmienić pierwotnej implementacji takiej metody, „wsuwając” do programu jej przesłoniętą wersję, która, powiedzmy, zawsze zwraca wartość true, wskazując pomyślną rejestrację użytkownika, niezależnie od tego, jakie hasło rzeczywiście wszedł. Masz prawo, jeśli pozwala na to konkretna sytuacja, pójść dalej i zadeklarować final
całą klasę jako klasę; metoda ValidatePassword
uzyska tę samą właściwość pośrednio. Użycie modyfikatora final
w deklaracji metody lub klasy nakłada poważne ograniczenia na możliwość dalszego wykorzystania i rozwoju kodu. Użycie final
metody w deklaracji jest pewnym wskaźnikiem, że implementacja metody jest samodzielna i całkowicie kompletna. Inni programiści, którzy będą chcieli wykorzystać Twoją klasę, rozbudowując jej funkcje do własnych potrzeb, będą ograniczeni w wyborze środków do osiągnięcia swojego celu lub całkowicie ich pozbawieni. Zaznaczając final
klasę jako całość, wyłączysz jej możliwość dziedziczenia i prawdopodobnie znacznie zmniejszysz jej użyteczność dla innych. Kiedy będziesz miał zamiar skorzystać z modyfikatora final
, upewnij się, czy jesteś gotowy na takie poświęcenia i czy warto je ponosić. W wielu przypadkach, aby osiągnąć wystarczający poziom bezpieczeństwa kodu, nie jest konieczne wyznaczanie całej klasy, gdyż final
całkiem możliwe jest zachowanie rozszerzalności klasy poprzez oznaczenie final
modyfikatorem jedynie jej „krytycznych” elementów konstrukcyjnych. W tym przypadku pozostawisz główne funkcje klasy niezmienione i jednocześnie pozwolisz na jej dziedziczenie z dodaniem nowych członków, ale bez ponownego definiowania „starych”. Oczywiście pola, do których kod metod ma dostęp, final
muszą z kolei być oznaczone jako final
albo private
, ponieważ w przeciwnym razie dowolna klasa pochodna będzie mogła zmienić ich zawartość, wpływając na zachowanie odpowiednich metod. Kolejnym efektem zastosowania modyfikatora final
jest uproszczenie rozwiązywanego przez kompilator problemu optymalizacji kodu. Tak się dzieje, gdy wywoływana jest metoda, która nie jest oznaczona jakofinal
, system wykonawczy określa rzeczywistą klasę obiektu, kojarzy wywołanie z najbardziej odpowiednim kodem z grupy przeciążonych metod i przekazuje kontrolę temu kodowi. Gdyby jednak na przykład metoda getName
z omawianego wcześniej przykładu klasy Attr
została oznaczona jako final
, operacja jej wywoływania mogłaby zostać zauważalnie uproszczona. W najbardziej trywialnym przypadku, takim jak ten dotyczący getName
, kompilator może po prostu zastąpić wywołanie metody jej kodem głównym. Mechanizm ten nazywa się wstawianiem kodu. Podczas korzystania z wbudowanej wersji metody getName
następujące dwa wyrażenia są wykonywane dokładnie tak samo:
system.out.println("id = " + rose.name);
system.out.println("id = " + rose.getName());
Chociaż powyższe wyrażenia są równoważne, to drugie nadal ma przewagę, ponieważ metoda getName
pozwala nadać polu nazwy właściwość tylko do odczytu, a kodowi klasy pewien stopień abstrakcji, co pozwala na bardziej swobodną zmianę implementacja klasy. Ten sam schemat optymalizacji może zostać zastosowany przez kompilator do metod private
i statiс
, ponieważ one również nie pozwalają na przesłonięcie. Użycie modyfikatora final
w deklaracjach klas zwiększa również wydajność niektórych operacji sprawdzania typów. W takim przypadku wiele takich operacji można wykonać już na etapie kompilacji, dzięki czemu potencjalne błędy zostaną wykryte znacznie wcześniej. Jeśli kompilator napotka w tekście źródłowym odwołanie do klasy final
, może być „pewne”, że odpowiadający mu obiekt jest określonego typu. Kompilator jest w stanie od razu określić miejsce zajmowane przez klasę w ogólnej hierarchii klas i sprawdzić, czy jest ono poprawnie użyte. Jeżeli modyfikator final
nie zostanie zastosowany, odpowiednie kontrole przeprowadzane są dopiero na etapie wykonywania programu. Ćwiczenie 3.4. Czy wskazane jest uwzględnienie końcowego modyfikatora w deklaracjach metod (a jeśli tak, to jakich) klas pojazdu i pojazdu osobowego ? Link do oryginalnego źródła: http://src-code.net/metody-i-klassy-final-Java
GO TO FULL VERSION