Почему невозможно инициализировать конечную переменную в блоке catch?

Пример 1

    final String someString = "Hello World";
    try {
      someString = "Hello";
    } catch (RuntimeException e) {
      someString = "World";
    }

Если мы скомпилируем этот код, мы получим две ошибки:

Невозможно присвоить значение конечной переменной 'someString' // Строка 3

Невозможно присвоить значение конечной переменной 'someString' // Строка 5

Это я понимаю. Это конечная переменная, вы уже присвоили ей ссылку и больше не можете ее изменить. Хороший.

Пример 2

    final String someString;
    try {
      someString = "Hello";
    } catch (RuntimeException e) {
      someString = "World";
    }

Если мы скомпилируем этот код, мы получим только одну ошибку:

Переменная 'someString' могла быть уже присвоена // строке 5

Вот этого я не понимаю. Нам разрешено инициализироваться в блоке try. Тем не менее, если что-то пошло не так, даже до фактической инициализации нам не разрешается инициализировать конечную переменную в блоке catch.

Почему это так? Я ожидал, что мы сможем инициализировать переменную в блоке catch, так как блок try все равно потерпел неудачу.

Такой вопрос «почему» по своей сути не имеет ответа. Пожалуйста, отредактируйте, чтобы уточнить, что вы подразумеваете под «почему». Например, какое поведение вы ожидали от кода вместо этого?
Sweeper 17.02.2023 10:40

После назначения переменные final не могут быть изменены

Sudhir Ojha 17.02.2023 10:41

@Sweeper разработал мой вопрос.

Keshavram Kuduwa 17.02.2023 10:45
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
3
64
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

Длинный ответ: переменная final должна быть назначена ровно один раз, прежде чем ее можно будет прочитать. Не менее (т.е. никогда не присваивался), не более (т.е. присваивался дважды). Ровно один раз.

По этой причине следующий код тривиально неверен:

final String someString;
someString = "Hello";
someString = "World";

Второе присваивание приведет к ошибке времени компиляции, так как someString определенно уже было присвоено.

Теперь давайте посмотрим на этот код:

final String someString;
try {
  callSomeMethod();
  someString = "Hello";
  callSomeOtherMethod();
} catch (Exception e) {
  someString = "World";
}

Теперь мы столкнулись с проблемой: компилятор не может знать, когда выброшено исключение, вызывающее выполнение блока catch: оно может быть выброшено в callSomeMethod() или в callSomeOtherMethod() (или через какую-то чудесную внутреннюю проблему даже при попытке присвоить константу).

Таким образом, компилятор просто не знает, было ли уже назначено someString внутри блока catch или нет (и оба варианта теоретически возможны во время выполнения!). Если переменная была присвоена, то присваивание должно быть ошибкой времени компиляции. Если бы это было не так, было бы хорошо. Но поскольку статический анализ не может сказать вам, какой именно, компилятор должен пометить все присваивание как ошибку.

Теоретически в некоторых крайних случаях компилятор теоретически может знать, что переменная определенно была назначена в блоке try, даже когда возникает исключение. В частности, если это первое действие, и оно может доказать, что действие присваивания не может вызвать исключение. Но если это так, то программист может просто переместить это присваивание из блока try и избежать всей проблемы. Вероятно, поэтому в JLS такого правила нет.

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

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