2010-04-08 3 views
8

Почему Java не позволяет мне присваивать значение конечной переменной в блоке catch после установки значения в блоке try, даже если оно не является возможно, чтобы окончательное значение было записано в случае исключения.Назначение значения по умолчанию для конечной переменной в случае исключения в Java

Вот пример, который демонстрирует проблему:

public class FooBar { 

    private final int foo; 

    private FooBar() { 
     try { 
      int x = bla(); 
      foo = x; // In case of an exception this line is never reached 
     } catch (Exception ex) { 
      foo = 0; // But the compiler complains 
        // that foo might have been initialized 
     } 
    } 

    private int bla() { // You can use any of the lines below, neither works 
     // throw new RuntimeException(); 
     return 0; 
    } 
} 

Проблема не трудно обойти, но я хотел бы понять, почему компилятор не принимает это.

Заранее благодарим за любые поступления!

+2

Хорошо, если вы поймаете общее «Исключение», возможно ли, что что-то произойдет сразу после/во время 'foo = x', что вызовет исключение? Может быть, компилятор «играет в безопасности»? – FromCanada

+0

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

+2

Где вы говорите: «В случае исключения эта строка никогда не доходит» Я подозреваю, что компилятор отказывается узнать ваши намерения на этот уровень детализации. Таким образом, все, что он видит, это foo, назначаемый дважды. Возможно, причина в том, что это позволяет компилятору оптимизировать код до foo = bla(), так как x в конечном итоге лишнее? Просто размышляю. – greim

ответ

7
try { 
    int x = bla(); 
    foo = x; // In case of an exception this line is never reached 
} catch (Exception ex) { 
    foo = 0; // But the compiler complains 
      // that foo might have been initialized 
} 

Причина в том, что компилятор не может сделать вывод о том, что исключение может быть брошено только перед foo можно инициализировать. Этот пример является частным случаем, когда очевидно, что это верно, но считает:

try { 
    int x = bla(); 
    foo = x; // In case of an exception this line is never reached...or is it? 
    callAnotherFunctionThatThrowsAnException(); // Now what? 
} catch (Exception ex) { 
    foo = 0; // But the compiler complains 
      // that foo might have been initialized, 
      // and now it is correct. 
} 

Чтобы написать компилятор для обработки очень конкретные случаев, как это будет огромная задача - там, скорее всего, очень многие из них.

+0

Я думаю, что проблема здесь. Назначение должно быть после любых утверждений, которые могут иметь побочные эффекты, которые, как я полагаю, не будут слишком сложными для проверки, но, с другой стороны, это не обязательно, так как вы можете легко обойти его (если немного уродливо). – Alfonso

0

Как насчет броска Error?

+0

Правильно, попробуйте поймать Throwable. Если это позволит вам. – bmargulies

+0

Это будет распространяться из блока try-catch и привести к исключению в вызывающем «new FooBar();». Таким образом, конечная переменная никогда не будет написана в любом случае. Но в моем случае компилятор жалуется, что переменная может быть написана дважды, что, безусловно, не так. – Alfonso

+0

Да, вы правы. Я неверно истолковал этот вопрос. Меня это тоже раздражает. – lexicore

2

Чтобы стать педантом, Thread.stop(Throwable) может вызвать исключение сразу после назначения блока try.

Однако правила с определенным назначением и смежными терминами достаточно сложны. Проверьте JLS. Попытка добавить больше правил усложнила бы язык и не обеспечила бы значительную выгоду.

+0

Вау, я не знал о Thread.stop (Throwable). Это должен быть самый злой метод во всем JDK ... – Alfonso

+0

+1 для упоминания JLS. Это не просто причуда реализации компилятора. – Antimony