Могут ли переменные экземпляра быть эффективно окончательными/Являются ли только локальные переменные окончательными в Java?

Я хочу использовать улучшение try-with-resources в Java 9, поместив ссылочную переменную внутрь try with resources вместо всего объявления переменной. Я также знаю, что для этого я должен следовать правилу: Variable used as a try-with-resources resource should be final or effectively final. Сначала я попробую с локальной, а затем с переменной экземпляра.

  1. Локальная переменная:

-Я делаю переменную final, которая следует заданному правилу и отлично компилируется:

public static void main (String[] args) throws IOException{
        final FileWriter fw = new FileWriter ("test.txt");
        try(fw) {
            //some code
        }
    }

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

public static void main (String[] args) throws IOException{
     FileWriter fw = new FileWriter ("test.txt");
    try(fw) {
        //some code
    }
}
  1. Переменная экземпляра:

Но будет ли этот же шаблон работать и для переменной экземпляра? Давай попробуем.

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

public class Test {
  final FileWriter fw = new FileWriter ("a.txt");

    void m1() throws IOException {
        try(fw ) {
            //some code
        }
    }
}

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

public class Test {
   FileWriter fileWriter = new FileWriter ("a.txt");

    void m1() throws IOException {
        try(fileWriter) {
            //some code
        }
    }
}

Это дает мне сообщение: Переменная, используемая в качестве ресурса попытки с ресурсами, должна быть окончательной или фактически окончательной. Итак, после всего этого, я возвращаюсь к своему первому вопросу. Может ли переменная экземпляра когда-либо быть фактически final или этот термин используется только для локальных переменных? Как я только что показал, я никогда не изменяю переменную (которую следует считать фактически окончательной), однако компилятор никогда не угрожает ей как таковой.


person Stefan    schedule 12.11.2020    source источник
comment
Только локальные переменные могут быть эффективными final.   -  person Andreas    schedule 12.11.2020
comment
Я подозреваю, что это связано с возможными подклассами, имеющими к нему доступ. Если вы сделаете его приватным, он все равно будет жаловаться?   -  person Gus    schedule 12.11.2020
comment
@Андреас Это то, что я надеялся услышать. Любой документ, подтверждающий этот комментарий, пожалуйста? Я пока ничего не нашел.   -  person Stefan    schedule 12.11.2020
comment
@Gus Да, он все еще жалуется.   -  person Stefan    schedule 12.11.2020
comment
В Java модификатор доступа не подразумевает доступ по умолчанию, что означает, что ЛЮБОЙ класс в этом пакете имеет доступ к этому члену данных. Следовательно, он не является фактически окончательным просто потому, что он не изменен из самого объявляющего класса. Его можно изменить вне класса. Это в основном ОБЩЕСТВЕННО для пакета.   -  person hfontanez    schedule 12.11.2020
comment
Он все еще жалуется, потому что он не местный. Следовательно, он ДОЛЖЕН быть объявлен как final. Не требуется, если переменная объявлена ​​локально.   -  person hfontanez    schedule 12.11.2020
comment
@hfontanez Имеет смысл. Итак, мой ответ заключается в том, что только локальные переменные являются фактически окончательными, как сказал Андреас?   -  person Stefan    schedule 12.11.2020
comment
Переменная, используемая в качестве ресурса попытки с ресурсами, должна быть окончательной (для глобальных полей) или фактически окончательной (для локальных).   -  person hfontanez    schedule 12.11.2020


Ответы (1)


Спецификация языка Java, раздел 4.12.4. final Переменные, четко указывает, что только локальные переменные и параметры могут быть фактически окончательными:

Некоторые переменные, которые не объявлены final, вместо этого считаются фактически окончательными:

  • локальная переменная, декларатор которой имеет инициализатор (§14.4.2) является фактически окончательным, если выполняются все следующие условия:

    • Не объявлено final.

    • Он никогда не встречается в левой части выражения присваивания (§15.26). (Обратите внимание, что декларатор локальной переменной, содержащий инициализатор, не является выражением присваивания.)

    • Он никогда не используется в качестве операнда префиксного или постфиксного оператора увеличения или уменьшения (§15.14, §15.15).

  • локальная переменная, в объявлении которой отсутствует инициализатор, является фактически final, если выполняются все следующие условия:

    • Не объявлено final.

    • Всякий раз, когда он встречается в левой части выражения присваивания, он определенно не присваивается и не присваивается определенно перед присваиванием; то есть он определенно не назначен и не назначен определенно после правой части выражения присваивания (§16 (Определенное назначение)).

    • Он никогда не используется в качестве операнда префиксного или постфиксного оператора инкремента или декремента.

  • Метод, конструктор, лямбда-выражение или параметр исключения (§8.4.1, §8.8.1, §9.4, §15.27.1, §14.20) обрабатывается с целью определения того, является ли он действительно окончательным, как локальная переменная чей декларатор имеет инициализатор.

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

person Andreas    schedule 12.11.2020