Явное "пустое конечное поле", возможно, не было инициализировано "Исключение, вызванное нечетностью метода

У меня есть код вроде:

final int var1;    

if ( isSomethingTrue ) {

   var1 = 123;

} else {
   throwErrorMethod();
}

int var2 = var1;

И throwErrorMethod определяется примерно так:

private void throwErrorMethod() throws Exception{

   throw new Exception();

}

И я получаю компиляцию blank final field may not have been initialized для оператора var2 = var1. Если я встраиваю метод, компиляция в порядке!

  • Разве компилятор не видит throws Exception в методе, который называется?
  • Почему ошибка, которая содержит слово may в ней, останавливает компиляцию?!?

Ответы

Ответ 1

  • Нет, компилятор не определяет, что throwErrorMethod никогда не завершится нормально. В спецификации нет ничего, что можно было бы предложить. К сожалению, нет способа указать, что метод никогда не будет нормально возвращаться.

  • Он только "может", потому что существует потенциальный путь выполнения, который не инициализирует переменную. Наличие такого пути выполнения определяется как ошибка.

Вы можете найти эту пару сообщений в блоге (часть 1; часть 2) от Эрика Липперта. Это о С#, а не Java, но это тот же принцип.

Ответ 2

Исключения должны быть исключительными. Он не предполагает, что исключение всегда выбрасывается.

Компилятор использует слово may, поскольку он не может определить, можете ли вы получить доступ к неинициализированной переменной. Кроме того, вы можете изменить, что делает этот метод, не перекомпилировав этот класс, и любое допущение, которое оно сделало, было бы неверным.

Если вы хотите всегда выкидывать исключение, вы можете сделать

final int var1;    

if ( isSomethingTrue ) {

   var1 = 123;

} else {
   throw exceptionMethod();
}

int var2 = var1;

// later
public Exception exceptionMethod() {
    return new Exception("Complex-Exception-String");
}

Ответ 3

Компилятор не выполняет такую ​​проверку, которую вы ожидаете. Он не уверен, что throwErrorMethod действительно генерирует исключение каждый раз. Поэтому он предполагает, что можно перейти в ваше предложение else, вызвать throwErrorMethod, возврат из этого метода, а затем не инициализировать var1 (который должен быть инициализирован).

Ответ 4

Если вы хотите сообщить компилятору, что ошибка определенно будет выбрана, но не хотите вставлять логику для построения ошибки, вы можете сделать что-то вроде этого:

} else {
   throw createErrorMethod();
}

где createErrorMethod() объявляется как возвращающий некоторый тип Throwable.

Ответ 5

Метод, объявленный "throws Exception", не должен бросать это исключение в любой путь выполнения. Поэтому компилятор не знает, будет ли исключение исключаться методом и предполагает также нормальное завершение. Таким образом, может быть, что var1 не инициализируется.