Почему попытка использования ресурсов не работает с полевыми переменными?

Это мой самый первый вопрос о SO, и я смущен, что еще нет подобного вопроса!

Итак, вопрос:

Почему попытка использования ресурсов не работает с полевыми переменными?

Или, другими словами: почему для этого мне всегда нужна локальная переменная?

Вот пример кода:

public class FileWriteTest {

    public FileWriter file;

    public void workingDemo() {

        try(FileWriter file = new FileWriter(new File("someFilePath")) {
            // do something
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public void notWorkingDemo() {

        file = null;

        try(file = new FileWriter(new File("someFilePath")) {
            // do something
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

Может ли кто-нибудь объяснить мне, почему существует эта конвенция?


person ConcurrentHashMap    schedule 21.06.2013    source источник


Ответы (6)


Переменная экземпляра может быть изменена в любой момент выполнения блока try-with-resources. Это нарушит его инвариант и предотвратит очистку. Обратите внимание, что локальная переменная неявно является final по той же причине.

Кстати, лучший вопрос: почему Java заставляет объявлять локальную переменную, даже если мы не ссылаемся на нее внутри блока. C#, например, этого не требует.

Обновление: с версией 9 Java перестала заставлять нас:

private final Some obj = new Some();

try (obj) { 
  // obj captured in a hidden local variable, resource closed in the end
}
person Marko Topolnik    schedule 21.06.2013

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

person Peter Lawrey    schedule 21.06.2013
comment
Так тоже можно убежать. Суть в том, чтобы иметь конечную переменную, которая гарантированно будет хранить одно и то же значение до конца попытки с ресурсами. - person Marko Topolnik; 21.06.2013
comment
@MarkoTopolnik Являются ли поля try неявно окончательными? - person Peter Lawrey; 21.06.2013
comment
Да, только что попробовал. Переменные, объявленные в try-with-resources, неявно являются окончательными. В частности, Eclipse выдает эту ошибку: Невозможно назначить ресурс ‹var-name› инструкции try-with-resources. Не то же самое, что для конечной переменной. - person Marko Topolnik; 21.06.2013
comment
@MarkoTopolnik Я этого не знал. Это имеет смысл. С другой стороны, я бы предпочел, чтобы переменные были неявно окончательными, если только ключевое слово не использовалось, чтобы сделать их не окончательными. например var - person Peter Lawrey; 21.06.2013
comment
+1 Точно. По крайней мере, 90% всех переменных, объявленных в коде Java, являются окончательными. Особенно болезненно объявлять все параметры метода final, хотя 99% из них являются окончательными. - person Marko Topolnik; 21.06.2013
comment
@MarkoTopolnik Это побудило бы разработчиков предпочесть конечные переменные неконечным или, по крайней мере, заставило бы их задуматься об этом. - person Peter Lawrey; 21.06.2013
comment
Они почти наверняка оставят свои вары окончательными, за исключением тех, которые им конкретно нужно изменить. Кроме того, это, вероятно, будет препятствовать идиомам, связанным с изменяемыми переменными. Невероятно, насколько меньше когнитивного напряжения я получаю от кода с final vars! - person Marko Topolnik; 21.06.2013

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

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

person Jeff Foster    schedule 21.06.2013

В Java 9 добавлена ​​поддержка try с ресурсами с переменными.

    // Original try-with-resources statement from JDK 7 or 8
    try (Resource r1 = resource1;
         Resource r2 = resource2) {
        // Use of resource1 and resource 2 through r1 and r2.
    }

    // New and improved try-with-resources statement in JDK 9
    try (resource1;
         resource2) {
        // Use of resource1 and resource 2.
    }

https://blogs.oracle.com/darcy/more-concise-try-with-resources-statements-in-jdk-9

person miskender    schedule 16.02.2018

Во-первых, я думаю, что было бы плохой практикой иметь переменную/ресурс, который используется в нескольких местах. Если он не открыт в try, то вы не сможете его потом закрыть, если он там открыт, то нелокальная переменная вам не понадобится. Это приводит к «второму»: если у вас уже есть открытый ресурс, вам нужно явно закрыть его где-то еще, иначе автозакрытие не узнает, открыт он или нет.

Так что, ИМХО, имеет смысл обращаться с ним только так, как указано в спецификации.

person Dominik Sandjaja    schedule 21.06.2013

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

anything
{
int var;
}

// cannot access var from here!

Почему try {} должен быть исключением?

person SiN    schedule 21.06.2013