Присвоение эффективно конечной переменной в операторе 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 или что-то подобное.

Другие вопросы по тегам