Присвоение эффективно конечной переменной в операторе try/catch
Следующий код не компилируется с javac 1.8.0_144 и ecj:
private LongSupplier foo() {
long fileSize;
try {
fileSize = canThrow();
} catch (IOException e) {
fileSize = 42;
}
LongSupplier foo = () -> 1 + fileSize;
return foo;
}
Мне интересно, если это ошибка в компиляторе. Определение эффективно окончательного в JLS:
Некоторые переменные, которые не объявлены как final, вместо этого считаются эффективными final:
Локальная переменная, у которой в объявителе есть инициализатор (§14.4.2), фактически является окончательной, если все из следующего верно:
Это не объявлено окончательным.
Это никогда не встречается как левая часть в выражении присваивания (§15.26). (Обратите внимание, что декларатор локальной переменной, содержащий
инициализатор не является выражением присваивания.)Он никогда не встречается как операнд префиксного или постфиксного оператора увеличения или уменьшения (§15.14, §15.15).
Локальная переменная, в деклараторе которой отсутствует инициализатор, фактически является окончательной, если выполняются все следующие условия:
Это не объявлено окончательным.
Всякий раз, когда это происходит как левая часть в выражении присваивания, оно определенно не присваивается и не обязательно присваивается перед присваиванием; то есть он определенно не назначен и не определенно назначен после правой части выражения присваивания (§16 (Определенное присвоение)).
Он никогда не встречается как операнд префиксного или постфиксного оператора увеличения или уменьшения.
Метод, конструктор, лямбда или параметр исключения (§8.4.1, §8.8.1, §9.4, §15.27.1, §14.20) рассматриваются с целью
определение, является ли оно эффективно окончательным, как локальная переменная
чей декларатор имеет инициализатор.
Мое чтение заключается в том, что в пункте 2 назначения в блоке try/catch разрешены, потому что fileSize
определенно не назначен до назначения.
Я думаю, что причина для отказа от кода:
- fileSize определенно не назначен перед блоком try
- fileSize назначается (определенно? Кажется, что 16.1.8 не заботится об исключениях в назначении) после fileSize = canThrow ()
- fileSize назначается после блока try
- fileSize не определенно не назначен перед блоком catch, и, следовательно, не определенно не назначен перед назначением в блоке catch.
- таким образом, пункт 2 из 4.12.4 здесь не применяется
Это правильно?
2 ответа
Определение "Эффективно окончательный" гласит, что добавление final
Модификатор не должен ничего менять. Давайте сделаем это и получим более четкую ошибку:
error: variable fileSize might already have been assigned
fileSize = 42;
^
Так что это точно такой же случай, как и при окончательном назначении переменной с помощью try / catch (что также дает обходной путь с использованием второй конечной переменной), а именно, переменная появляется слева от присваивания, что означает, что она не определенно не назначена.
(Для хорошего порядка try-catch не имеет ничего общего с проблемой: они просто утверждают, что параметр catch catch считается окончательным.)
Намерение быть "эффективно окончательным" основано на наличии двух потоков, каждый из которых имеет копию переменной с одинаковым именем. Время жизни этих двух потоков / переменных различно. Они хотят предотвратить изменения в одном потоке, которые потребовали бы некоторой синхронизации и проверки жизнеспособности.
Таким образом, они определенно не хотят назначения. Как решение языка дизайна.
Действительно внутренняя нить в canThrow could
использование fileSize
будучи все еще 0 после перехвата, установите другую переменную fileSize
до 42. Я думаю, что вы считаете возбужденное исключение, чтобы показать, что другой поток мертв.
В этом случае вы хотите Future/FutureTask или что-то подобное.