1. Огляд
Хоча успадкування дозволяє нам використовувати повторно існуючий код, іноді нам потрібно встановити обмеження на розширюваність з різних причин; остаточне ключове слово дозволяє зробити саме це.
У цьому підручнику ми розглянемо, що означає кінцеве ключове слово для класів, методів та змінних.
2. Підсумкові класи
Класи, позначені як остаточні, продовжувати не можна. Якщо ми подивимося на код базових бібліотек Java, то там ми знайдемо багато заключних класів. Одним із прикладів є клас String .
Розглянемо ситуацію, якщо ми можемо розширити клас String , замінити будь-який з його методів і замінити всі екземпляри String екземплярами нашого конкретного підкласу String .
Тоді результат операцій над об'єктами String стане непередбачуваним. А враховуючи те, що клас String використовується всюди, це неприпустимо. Тому клас String позначений як остаточний .
Будь-яка спроба успадкування від остаточного класу спричинить помилку компілятора. Щоб продемонструвати це, давайте створимо фінальний клас Cat :
public final class Cat { private int weight; // standard getter and setter }
І спробуємо його розширити:
public class BlackCat extends Cat { }
Ми побачимо помилку компілятора:
The type BlackCat cannot subclass the final class Cat
Зверніть увагу , що остаточне ключове слово в оголошенні класу не означає , що об'єкти цього класу є незмінними . Ми можемо вільно змінювати поля об'єкта Cat :
Cat cat = new Cat(); cat.setWeight(1); assertEquals(1, cat.getWeight());
Ми просто не можемо продовжити його.
Якщо ми неухильно дотримуємось правил гарного дизайну, нам слід ретельно створити та задокументувати клас або оголосити його остаточним з міркувань безпеки. Однак слід бути обережними при створенні підсумкових занять.
Зверніть увагу, що складання фіналу класу означає, що жоден інший програміст не може його вдосконалити. Уявіть, що ми використовуємо клас і не маємо для нього вихідного коду, і є проблема з одним методом.
Якщо клас остаточний, ми не можемо продовжити його, щоб замінити метод і вирішити проблему. Іншими словами, ми втрачаємо розширюваність - одну з переваг об’єктно-орієнтованого програмування.
3. Заключні методи
Методи, позначені як остаточні, не можна замінити. Коли ми розробляємо клас і відчуваємо, що метод не слід замінювати, ми можемо зробити цей метод остаточним . Ми також можемо знайти багато кінцевих методів у основних бібліотеках Java.
Іноді нам не потрібно повністю забороняти розширення класу, а лише запобігати перевизначенню деяких методів. Хорошим прикладом цього є клас Thread . Законно розширити його і таким чином створити власний клас потоку. Але його IsAlive () методу є остаточною .
Цей метод перевіряє, чи є потік живим. Неможливо перевизначити метод isAlive () правильно з багатьох причин. Один з них полягає в тому, що цей метод є рідним. Власний код реалізований іншою мовою програмування і часто специфічний для операційної системи та обладнання, на якому він працює.
Давайте створимо клас Dog і зробимо його метод sound () остаточним :
public class Dog { public final void sound() { // ... } }
Тепер розширимо клас Dog і спробуємо перевизначити його метод sound () :
public class BlackDog extends Dog { public void sound() { } }
Ми побачимо помилку компілятора:
- overrides com.baeldung.finalkeyword.Dog.sound - Cannot override the final method from Dog sound() method is final and can’t be overridden
Якщо деякі методи нашого класу викликаються іншими методами, ми повинні розглянути можливість зробити так звані методи остаточними . В іншому випадку заміщення їх може вплинути на роботу абонентів і призвести до дивовижних результатів.
Якщо наш конструктор викликає інші методи, ми, як правило, повинні оголосити ці методи остаточними з вищевказаної причини.
Яка різниця між тим, як зробити усі методи випускного класу і маркуванням самого випускного класу ? У першому випадку ми можемо розширити клас і додати до нього нові методи.
У другому випадку ми не можемо цього зробити.
4. Кінцеві змінні
Неможливо перепризначити змінні, позначені як остаточні . Після ініціалізації остаточної змінної її неможливо змінити.
4.1. Кінцеві примітивні змінні
Давайте оголосимо примітивну кінцеву змінну i, а потім призначимо їй 1.
І спробуємо призначити йому значення 2:
public void whenFinalVariableAssign_thenOnlyOnce() { final int i = 1; //... i=2; }
Компілятор каже:
The final local variable i may already have been assigned
4.2. Остаточні посилальні змінні
Якщо у нас є остаточна посилальна змінна, ми також не можемо її перепризначити. Але це не означає, що об'єкт, на який він посилається, незмінний . Ми можемо вільно змінювати властивості цього об’єкта.
Щоб продемонструвати це, давайте оголосимо остаточну посилальну змінну cat та ініціалізуємо її:
final Cat cat = new Cat();
Якщо ми спробуємо перепризначити його, ми побачимо помилку компілятора:
The final local variable cat cannot be assigned. It must be blank and not using a compound assignment
Але ми можемо змінити властивості екземпляра Cat :
cat.setWeight(5); assertEquals(5, cat.getWeight());
4.3. Заключні поля
Кінцеві поля можуть бути як константами, так і полями для одноразового запису. Щоб їх розрізнити, слід поставити запитання - чи включимо ми це поле, якби ми мали серіалізувати об’єкт? Якщо ні, то це не частина об’єкта, а константа.
Зверніть увагу, що згідно з правилами іменування, константи класу мають бути великими, а компоненти розділені символами підкреслення ("_"):
static final int MAX_WIDTH = 999;
Зверніть увагу, що будь-яке останнє поле повинно бути ініціалізоване до того, як конструктор завершиться .
Для статичних кінцевих полів це означає, що ми можемо їх ініціалізувати:
- після декларації, як показано у наведеному вище прикладі
- у блоці статичного ініціалізатора
Наприклад, кінцеві поля, це означає, що ми можемо їх ініціалізувати:
- після декларації
- в блоці ініціалізації екземпляра
- у конструкторі
В іншому випадку компілятор видасть нам помилку.
4.4. Заключні аргументи
Остаточне ключове слово також законно ставити перед аргументами методу. Остаточний аргумент не може бути змінений всередині методу :
public void methodWithFinalArguments(final int x) { x=1; }
Вищевказане призначення викликає помилку компілятора:
The final local variable x cannot be assigned. It must be blank and not using a compound assignment
5. Висновок
У цій статті ми дізналися, що означає ключове слово final для класів, методів та змінних. Хоча ми не можемо часто використовувати кінцеве ключове слово у своєму внутрішньому коді, це може бути гарним дизайнерським рішенням.
Як завжди, повний код цієї статті можна знайти в проекті GitHub.