Рубрики
Без рубрики

Финал против эффективного финала на Java

Узнайте разницу между Final и Эффективно Final в Java.

Автор оригинала: baeldung.

1. введение

Одна из самых интересных функций, введенных в Java 8, фактически является окончательной. Это позволяет нам не писать окончательный модификатор для переменных, полей и параметров, которые эффективно обрабатываются и используются как окончательные.

В этом уроке мы рассмотрим эту функцию origin и то, как она обрабатывается компилятором по сравнению с ключевым словом final . Кроме того, мы рассмотрим решение для использования в отношении проблемного случая использования эффективно конечных переменных.

2. Эффективное Конечное Происхождение

Проще говоря, объекты или примитивные значения являются фактически окончательными, если мы не изменим их значения после инициализации . В случае объектов, если мы не изменяем ссылку на объект, то она фактически является окончательной — даже если происходит изменение состояния объекта, на который ссылается ссылка.

До его введения мы не могли использовать конечную локальную переменную в анонимном классе . Мы по-прежнему не можем использовать переменные, которым присвоено более одного значения внутри анонимных классов, внутренних классов и лямбда-выражений. Введение этой функции позволяет нам не использовать модификатор final для переменных, которые фактически являются окончательными, экономя нам несколько нажатий клавиш.

Анонимные классы являются внутренними классами, и они не могут получить доступ к неконечным или неконечным переменным или мутировать их в своих охватывающих областях, как указано в JLS 8.1.3. То же ограничение применяется к лямбда-выражениям, поскольку наличие доступа потенциально может привести к проблемам параллелизма.

3. Финал против эффективного Финала

Самый простой способ понять, является ли конечная переменная фактически окончательной, – это подумать, позволит ли удаление ключевого слова final скомпилировать и запустить код:

@FunctionalInterface
public interface FunctionalInterface {
    void testEffectivelyFinal();
    default void test() {
        int effectivelyFinalInt = 10;
        FunctionalInterface functionalInterface 
            = () -> System.out.println("Value of effectively variable is : " + effectivelyFinalInt);
    }
}

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

3.1. Обработка компилятора

JLS 4.12.4 утверждает, что если мы удалим модификатор final из параметра метода или локальной переменной без ошибок во время компиляции, то он фактически является окончательным. Более того, если мы добавим ключевое слово final к объявлению переменной в допустимой программе, то она фактически будет окончательной.

Компилятор Java не выполняет дополнительной оптимизации для конечных переменных, в отличие от переменных final .

Давайте рассмотрим простой пример, который объявляет две переменные final String , но использует их только для объединения:

public static void main(String[] args) {
    final String hello = "hello";
    final String world = "world";
    String test = hello + " " + world;
    System.out.println(test);
}

Компилятор изменит код, выполняемый в главный метод выше, чтобы:

public static void main(String[] var0) {
    String var1 = "hello world";
    System.out.println(var1);
}

С другой стороны, если мы удалим окончательные модификаторы , переменные будут считаться фактически окончательными, но компилятор не удалит их , поскольку они используются только для конкатенации.

4. Атомная модификация

Как правило, не рекомендуется изменять переменные, используемые в лямбда-выражениях и анонимных классах . Мы не можем знать, как эти переменные будут использоваться внутри блоков методов. Их мутация может привести к неожиданным результатам в многопоточных средах.

У нас уже есть учебник, объясняющий лучшие практики при использовании лямбда-выражений, и еще один, который объясняет общие анти-шаблоны при их изменении . Но есть альтернативный подход, который позволяет нам изменять переменные в таких случаях, что обеспечивает потокобезопасность за счет атомарности.

Пакет java.util.concurrent.atomic предлагает такие классы, как AtomicReference и AtomicInteger . Мы можем использовать их для атомарного изменения переменных внутри лямбда-выражений:

public static void main(String[] args) {
    AtomicInteger effectivelyFinalInt = new AtomicInteger(10);
    FunctionalInterface functionalInterface = effectivelyFinalInt::incrementAndGet;
}

5. Заключение

В этом уроке мы узнали о наиболее заметных различиях между final и фактически конечными переменными. Кроме того, мы предоставили безопасную альтернативу, которая позволяет нам изменять переменные внутри лямбда-функций.