Подтвердить что ты не робот

Компилятор говорит: "Переменная может не быть инициализирована", хотя у меня есть переменная флага, чтобы гарантировать ее

Вот мой фрагмент кода:

Someclass someObject;
boolean success = true;
try {
    someObject = someOperation();
} catch (Exception e) {
    success = false;
}
if (success) {
    int number = Integer.valueOf(someObject.someMethord());
}

и внутри строки:

 int number = Integer.valueOf(someObject.someMethord());

выскакивает компилятор Java и говорит об ошибке

Ошибка: переменная someObject, возможно, не была инициализирована`.

Однако, если success равно true, тогда нет способа someObject не инициализироваться, почему я получаю эту ошибку?

4b9b3361

Ответ 1

Компилятор не анализирует связь между вашим флагом success и инициализацией переменной someObject.

Что касается компилятора, someObject может не инициализироваться, если возникает исключение.

Вы можете решить эту проблему, установив переменную в null в вашем блоке catch (и вместо проверки вашей переменной success убедитесь, что someObject != null).

Или вы можете переместить оператор int number = Integer.valueOf(someObject.someMethord()); внутри блока try.

Ответ 2

Спецификация языка Java (JLS) точно определяет, как компилятор должен анализировать такой код.

В вашем случае локальная переменная someObject не является определенно назначенной, прежде чем она будет использоваться в блоке if. Определенное назначение, описанное в главе 16 JLS, определяет точные правила, по которым переменная может считаться назначенной ( "initialized" ).

Он анализирует операторы try и if отдельно. После try, someObject определенно не назначается, потому что он не назначен в блоке catch. В if условие может быть true или false. Если это было true, вы получите сообщение об ошибке, потому что someObject не определенно назначено в этот момент.

Компилятор Java не разрешен, чтобы проанализировать этот код и "выяснить", что success может быть истинным, только если if someObject, потому что в языковых правилах предписывается точный анализ, который должен выполняться. Это не случай, когда компилятор недостаточно интеллектуальный - это случай, когда стандарт языка Java является строгим.

Обратите внимание: если вы используете if(false) вместо if(success), вы не получите ошибку, потому что JLS указывает, что false является константным выражением и, следовательно, тело цикла никогда не будет выполняться.


Переменная флага в любом случае не нужна. Перемещение зависимого кода в try или установка переменной в null в декларации и явная проверка на someObject != null - это все подходы, которые проще понять и гораздо менее подвержены ошибкам.

Ответ 3

Вы можете изменить свое объявление следующим образом:

Someclass someObject = null;

Или вы можете сделать все в try-catch, чтобы убедиться, что someObject будет правильно инициализирован

try {
    Someclass someObject = someOperation();
    int number = Integer.valueOf(someObject.someMethod());
} catch (Exception e) {
    //...
}

Ответ 4

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

Другая причина заключается в том, что компилятор работает быстро. Умный анализ важен для оптимизации, но его "дорого". Оптимизация - это не работа javac (они происходят во время выполнения), поэтому javac не беспокоит.

Он даже не распознает тривиальные случаи, такие как

int f(boolean b) {
    if (b) {
        return 1;
    } else if (!b) {
        return 0;
    }
}

Правила указаны в JLS Chapter Definite Assignment.

Ответ 5

Вы можете просмотреть этот сценарий как -

What happens is your try blocks fails to initialize the someObject.

Это означает, что может быть какое-то исключение в методе someOperation(). Исключение будет обнаружено, но someObject не будет инициализироваться.

Вы можете исправить это, установив someObject в null или new SomeObject() в блок catch.

Ответ 6

То, что вы описываете в Java, очень похоже на С# (там будет сообщение CS0165 Use of unassigned local variable 'someObject'). Решение на обоих языках состоит в создании нулевого присвоения:

      SomeClass someObject = null;

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

Пример Java:

class SomeClass
{
    public int someMethod()
    {
      return 1;
    }
}

public class JavaFiddle
{

    public static SomeClass someOperation()
    {
        SomeClass result = new SomeClass();
        return result;
    }

    public static void main(String[] args)
    {
      SomeClass someObject = null;
        boolean success = true;
        try {
            someObject = someOperation();
        } catch (Exception e) {
         success = false;
        }
        if (success) {
            int number = Integer.valueOf(someObject.someMethod());
        }
    }

}

Вставьте этот код в JavaFiddle
(нажмите Ctrl и left-click, чтобы открыть вкладку с JavaFiddle)

> Компиляция...
> Запуск...

Как вы можете видеть, в приведенном выше коде проблема уже исправлена, поэтому, если вы вставляете ее в инструмент JavaFiddle, она будет работать как ожидалось без ошибок.

Чтобы спровоцировать ошибку, измените код следующим образом:

SomeClass someObject; // = null;

и запустите его снова. Вы получите сообщение:

> Компиляция...

/str/JavaFiddle.java:29: ошибка: переменная someObject, возможно, не была инициализирована
           int number = Integer.valueOf(someObject.someMethod());
                                                ^
1 ошибка


Примечание, что я только что привел пример, который вы предоставили "как есть" - вы должны рассмотреть намеки, касающиеся обработки исключений (см. комментарий к вашему вопросу от Эрика Липперта). Проглатывание исключений, не обрабатывающих их, как правило, не очень хорошо - лучше справляйтесь с ситуациями, которые вы знаете в коде, и позвольте вызывающему агенту обрабатывать любые исключения, которые могут возникнуть, о которых вы не знаете (и, конечно же, реализуете некоторые протоколирования).

Ответ 7

компилятор имеет ограниченное представление о том, что может иметь эта переменная, например:

 public void test() {
    int y = 2;
    Integer x;
    if (y == 2) {
        x = 2;
    }

    System.out.println(x);// this will fail
}

Но сделайте это final y = 2, и компилятор увидит, что в этом случае единственное возможное значение y равно 2.