Я заметил, что это не будет компилироваться:

PrintWriter printWriter = new PrintWriter("test.txt");
printWriter.append('a');
printWriter.close();
printWriter = null;

try(printWriter = new PrintWriter("test.txt")) {

}

С: Error:(17, 24) java: <identifier> expected

На данный момент работает только новая переменная:

try(PrintWriter printWriter2 = new PrintWriter("test.txt")) {

}

Моя интуиция такова, что требуется только новый объект, но, видимо, нужна новая ссылка. Ясно, что это результат проверки типов во время компиляции, но почему старая ссылка не работает?

1
jordanpg 13 Окт 2014 в 05:19

2 ответа

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

JLS 14.20.3. try-with-resources говорит частично (выделено жирным шрифтом),

ResourceSpecification объявляет одну или несколько локальных переменных с выражениями инициализатора, которые действуют как ресурсы для оператора try.

Ресурс, объявленный в ResourceSpecification, неявно объявляется окончательным (§4.12.4), если он не объявлен явно окончательным.

Тип переменной, объявленной в ResourceSpecification, должен быть подтипом AutoCloseable, иначе произойдет ошибка времени компиляции.

2
Community 20 Июн 2020 в 12:12
Какова связь между закрываемым объектом и локальной переменной? Ссылки во внешней области могут быть закрыты, а затем повторно инициализированы.
 – 
jordanpg
13 Окт 2014 в 06:00

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

TryWithResourcesStatement:
    try ResourceSpecification Block Catchesopt Finallyopt

ResourceSpecification:
    ( Resources ;opt )

Resources:
    Resource
    Resource ; Resources

Resource:
    VariableModifiers[opt] Type VariableDeclaratorId = Expression

Мы видим, что для TryWithResourcesStatement требуется ResourceSpecification, который представляет собой один или несколько Resource, заключенных в круглые скобки и разделенных точкой с запятой. Для каждого ресурса требуется тип и имя (Type и VariableDeclaratorId).

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

1
David Waters 13 Окт 2014 в 05:41
С точки зрения спецификации, я думаю, что в ответе @Elliot ключевое слово выделено жирным шрифтом. Если переменная должна быть локальной, то это объясняет, почему необходим идентификатор типа.
 – 
jordanpg
13 Окт 2014 в 05:58
Я бы предположил, что важное слово в ответе @Elliots было объявлено в «A ResourceSpecification declares one or more…». Переменная в OP была локальной переменной (с областью действия в том же блоке, что и блок try-with-resource. Но сказать, что ResourceSpecification объявляет переменную, имеет очень конкретное значение. Опять же, это сводится к «потому что так сказано в спецификации» Эллиот предпочитает текстовое описание, я предпочитаю формальную грамматику, оба говорят одно и то же.
 – 
David Waters
13 Окт 2014 в 09:19