«Последнее» ключевое слово в Java

«Последнее» ключевое слово в Java

1. обзор

Хотя наследование позволяет нам повторно использовать существующий код, иногда нам действительно нужноset limitations on extensibility по разным причинам; ключевое словоfinal позволяет нам делать именно это.

В этом руководстве мы посмотрим, что означает ключевое словоfinal для классов, методов и переменных.

2. Final Классы

Classes marked as final can’t be extended. Если мы посмотрим на код основных библиотек Java, мы обнаружим там много классовfinal. Одним из примеров является классString.

Рассмотрим ситуацию, если мы можем расширить классString, переопределить любой из его методов и заменить все экземплярыString экземплярами нашего конкретного подклассаString.

Тогда результат операций над объектамиString станет непредсказуемым. А учитывая, что классString используется везде, это недопустимо. Вот почему классString отмечен какfinal.

Любая попытка наследования от классаfinal приведет к ошибке компилятора. Чтобы продемонстрировать это, давайте создадим классfinalCat:

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

Обратите внимание, чтоthe final keyword in a class declaration doesn’t mean that the objects of this class are immutable. Мы можем свободно изменять поля объектаCat:

Cat cat = new Cat();
cat.setWeight(1);

assertEquals(1, cat.getWeight());

Мы просто не можем продлить это.

Если мы строго следуем правилам хорошего дизайна, мы должны тщательно создавать и задокументировать класс или объявить егоfinal из соображений безопасности. Однако при создании классовfinal следует соблюдать осторожность.

Обратите внимание, что создание классаfinal означает, что никакой другой программист не может его улучшить. Представьте, что мы используем класс и у нас нет исходного кода для него, и есть проблема с одним методом.

Если это классfinal,, мы не можем расширить его, чтобы переопределить метод и устранить проблему. Другими словами, мы теряем расширяемость, одно из преимуществ объектно-ориентированного программирования.

3. Final Методы

Methods marked as final cannot be overridden. Когда мы проектируем класс и чувствуем, что метод не следует переопределять, мы можем сделать этот методfinal. Мы также можем найти множество методовfinal в основных библиотеках Java.

Иногда нам не нужно полностью запрещать расширение класса, а нужно только предотвратить переопределение некоторых методов. Хорошим примером этого является классThread. Разрешается расширять его и таким образом создавать собственный класс потока. Но его методыisAlive() - этоfinal.

Этот метод проверяет, жив ли поток. Правильно переопределить методisAlive() невозможно по многим причинам. Одним из них является то, что этот метод является родным. Нативный код реализован на другом языке программирования и часто зависит от операционной системы и оборудования, на котором он работает.

Давайте создадим классDog и сделаем его методsound()final:

public class Dog {
    public final void sound() {
        // ...
    }
}

Теперь давайте расширим классDog и попробуем переопределить его методsound():

public class BlackDog extends Dog {
    public void sound() {
    }
}

Мы увидим ошибку компилятора:

- overrides
com.example.finalkeyword.Dog.sound
- Cannot override the final method from Dog
sound() method is final and can’t be overridden

Если некоторые методы нашего класса вызываются другими методами, мы должны рассмотреть возможность создания вызываемых методовfinal. В противном случае их переопределение может повлиять на работу вызывающих абонентов и привести к неожиданным результатам.

Если наш конструктор вызывает другие методы, мы должны обычно объявлять эти методыfinal по указанной выше причине.

В чем разница между созданием всех методов классаfinal и маркировкой самого классаfinal? В первом случае мы можем расширить класс и добавить в него новые методы.

Во втором случае мы не можем этого сделать.

4. Final Переменные

Variables marked as final can’t be reassigned. После инициализации переменнойfinal ее нельзя изменить.

4.1. Final примитивные переменные

Давайте объявим примитивную переменнуюfinali,, а затем присвоим ей 1.

И давайте попробуем присвоить ему значение 2:

public void whenFinalVariableAssign_thenOnlyOnce() {
    final int i = 1;
    //...
    i=2;
}

Компилятор говорит:

The final local variable i may already have been assigned

4.2. Справочные переменныеFinal

Если у нас есть ссылочная переменнаяfinal, мы также не можем переназначить ее. Ноthis doesn’t mean that the object it refers to is immutable. Мы можем свободно изменять свойства этого объекта.

Чтобы продемонстрировать это, давайте объявим ссылочную переменнуюfinalcat и инициализируем ее:

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. Final Поля

Final fields can be either constants or write-once fields. Чтобы различать их, мы должны задать вопрос - включили бы мы это поле, если бы мы сериализовали объект? Если нет, то это не часть объекта, а константа.

Обратите внимание, что в соответствии с соглашениями об именах константы класса должны быть в верхнем регистре, а компоненты должны быть разделены символами подчеркивания («_»):

static final int MAX_WIDTH = 999;

Обратите внимание, чтоany final field must be initialized before the constructor completes.

Для полейstatic final это означает, что мы можем их инициализировать:

  • после объявления, как показано в примере выше

  • в блоке статического инициализатора

Например, поляfinal, это означает, что мы можем их инициализировать:

  • по заявлению

  • в блоке инициализатора экземпляра

  • в конструкторе

В противном случае компилятор выдаст нам ошибку.

4.4. Final Аргументы

Ключевое словоfinal также можно помещать перед аргументами метода. A final argument can’t be changed inside a method:

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 означает для классов, методов и переменных. Хотя мы не можем часто использовать ключевое словоfinal в нашем внутреннем коде, это может быть хорошим дизайнерским решением.

Как всегда, полный код этой статьи можно найти в папкеGitHub project.