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

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

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. Если я встраиваю метод, компиляция в порядке!

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

person krishnaz    schedule 13.04.2011    source источник


Ответы (5)


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

  2. Это только «может», потому что есть потенциальный путь выполнения, который не инициализирует переменную. Наличие такого пути выполнения определяется как ошибка.

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

person Jon Skeet    schedule 13.04.2011
comment
К сожалению, нет способа указать, что метод никогда не вернется в нормальном режиме. Это действительно прискорбно? - person Mark Peters; 13.04.2011
comment
@Марк: Да. Если бы вы могли указать компилятору enforce, чтобы он никогда не возвращался нормально, а затем использовать эту информацию позже, это сделало бы некоторые фрагменты кода более ясными... например, код в этом вопросе, возможно. Текущие правила затрудняют извлечение в метод блока кода, который не будет нормально завершен, просто из-за способа, который влияет на определенное присваивание. - person Jon Skeet; 13.04.2011
comment
Я читаю статью в блоге Эрика. Спасибо за ссылку. - person Mark Peters; 13.04.2011
comment
Хорошо, готово, и я вижу ценность, спасибо. Хотя по большей части меня отвлекали все отсылки к принцессе-невесте. - person Mark Peters; 13.04.2011

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

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

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

final int var1;    

if ( isSomethingTrue ) {

   var1 = 123;

} else {
   throw exceptionMethod();
}

int var2 = var1;

// later
public Exception exceptionMethod() {
    return new Exception("Complex-Exception-String");
}
person Peter Lawrey    schedule 13.04.2011

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

person Kirk Woll    schedule 13.04.2011

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

} else {
   throw createErrorMethod();
}

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

person Ted Hopp    schedule 13.04.2011

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

person Howard    schedule 13.04.2011