Почему javac жалуется на неинициализированную переменную? - PullRequest
15 голосов
/ 02 февраля 2010

Для этого кода Java:

String var;
clazz.doSomething(var);

Почему компилятор сообщает об этой ошибке:

Возможно, переменная 'var' не инициализирована

Я думал, что все переменные или ссылки были инициализированы в null. Зачем вам нужно:

String var = null;

??

Ответы [ 6 ]

23 голосов
/ 02 февраля 2010

Экземпляры и переменные класса инициализируются нулем (или 0), но локальные переменные - нет.

См. §4.12.5 из JLS для очень подробного объяснения, которое говорит в основном то же самое:

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

  • Каждая переменная класса, переменная экземпляра или компонент массива инициализируется значением по умолчанию при создании:
    • [вычеркнут из списка всех значений по умолчанию]
  • Каждый параметр метода инициализируется соответствующим значением аргумента, предоставленным инициатором метода.
  • Каждый параметр конструктора инициализируется соответствующим значением аргумента, предоставленным выражением создания экземпляра класса или явным вызовом конструктора.
  • Параметр обработчика исключений инициализируется для брошенного объекта, представляющего исключение.
  • Локальной переменной должно быть явно задано значение перед ее использованием, либо путем инициализации, либо с помощью присваивания, таким образом, который может быть проверен компилятором с использованием правил для определенного присваивания
7 голосов
/ 03 февраля 2010

Это потому, что Java очень полезна (насколько это возможно).

Он будет использовать ту же логику, чтобы поймать некоторые очень интересные крайние случаи, которые вы, возможно, пропустили. Например:

int x;

if(cond2)
   x=2;
else if(cond3)
   x=3;

System.out.println("X was:"+x);

Это не удастся, потому что был другой случай, который не был указан. Дело в том, что здесь обязательно должен быть указан еще один случай, даже если это просто ошибка (то же самое верно для условия по умолчанию: условие в выражении switch).

Что вам следует отнять, что интересно, никогда не инициализируйте ваши локальные переменные, пока вы не поймете, что вам действительно нужно это сделать. Если у вас есть привычка всегда говорить "int x = 0;" Вы предотвратите работу этого фантастического детектора "плохой логики". Эта ошибка сэкономила мне время не раз.

4 голосов
/ 03 февраля 2010

То же самое касается Билла К. Я добавляю:

Компилятор Java может защитить вас от причинения себе вреда, если не установить переменную перед использованием ее в функции.Таким образом, он явно НЕ устанавливает значение по умолчанию, как описывает Билл К.

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

Как говорит Билл, вы определенно НЕ должны привыкатьавтоматической инициализации переменных при их объявлении.Инициализируйте переменные только во время объявления, если это действительно имеет смысл в контексте вашей программы.Например, если в 99% случаев вы хотите, чтобы x было 42, но внутри некоторого условия IF вы можете обнаружить, что это особый случай, и x должно быть 666, тогда хорошо, начните с «int x = 42;»и внутри IF переопределить это.Но в более нормальном случае, когда вы вычисляете значение на основе каких-либо условий, не инициализируйте произвольное число.Просто заполните его расчетным значением.Затем, если вы допустите логическую ошибку и не сможете установить значение при некоторой комбинации условий, компилятор может сказать вам, что вы облажались, а не пользователь.

PS Я видел много неработающих программ, которыескажем, что-то вроде:

HashMap myMap=new HashMap();
myMap=getBunchOfData();

Зачем создавать объект для инициализации переменной, когда вы знаете, что быстро собираетесь выбросить этот объект через миллисекунду?Это просто пустая трата времени.

Редактировать

Если взять тривиальный пример, предположим, что вы написали это:

int foo;
if (bar<0)
  foo=1;
else if (bar>0)
  foo=2;
processSomething(foo);

Это броситошибка во время компиляции, потому что компилятор заметит, что когда bar == 0, вы никогда не устанавливаете foo, но затем пытаетесь использовать его.

Но если вы инициализируете foo фиктивным значением, например

int foo=0;
if (bar<0)
  foo=1;
else if (bar>0)
  foo=2;
processSomething(foo);

Тогда компилятор увидит, что независимо от значения bar, foo устанавливается на что-то, поэтому он не выдаст ошибку.Если вы действительно хотите, чтобы foo было 0, когда bar равен 0, то это нормально.Но если на самом деле произошло то, что вы подразумевали, что один из тестов был <= или> =, или вы хотели включить еще один финал, когда bar == 0, то вы обманули компилятор, чтобы он не обнаружил вашу ошибку.И, кстати, я думаю, что такая конструкция - плохой стиль кодирования: не только компилятор не может быть уверен в том, что вы хотели, но и будущий программист по обслуживанию.

1 голос
/ 25 февраля 2010

Мне нравится мнение Билла К. о том, чтобы позволить компилятору работать на вас - я упал в инициализацию каждой автоматической переменной, потому что это «казалось, что дело в Java».Я не смог понять, что переменные класса (то есть постоянные вещи, о которых беспокоятся конструкторы) и автоматические переменные (некоторые счетчики и т. Д.) Различаются, хотя ВСЕ это класс в Java.

Так что я вернулся иудалил инициализацию, которую я бы использовал, например

List <Thing> somethings = new List<Thing>(); 
somethings.add(somethingElse); // <--- this is completely unnecessary

Ницца.Я получал предупреждение компилятора для

List<Thing> somethings = new List(); 

и думал, что проблема в отсутствии инициализации.НЕПРАВИЛЬНО.Проблема заключалась в том, что я не понимал правила, и мне нужно было <Thing>, идентифицированное в «новом», а не какие-либо созданные элементы типа <Thing>.

(Далее мне нужно научиться вставлять буквенные знаки «меньше и больше» в HTML!)

0 голосов
/ 26 февраля 2010

Конечно, если у вас действительно две строки друг над другом, как вы показываете - объявите это, заполните его, нет необходимости в конструкторе по умолчанию. Но, например, если вы хотите объявить что-то один раз и использовать это несколько или много раз, уместно использовать конструктор по умолчанию или нулевое объявление. Или указатель на объект настолько легкий, что лучше размещать его снова и снова внутри цикла, потому что выделение указателя намного меньше, чем создание объекта? (Предположительно, есть веская причина для нового объекта на каждом шаге цикла).

Билл IV

0 голосов
/ 25 февраля 2010

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

MyClass cls;
if (condition) {
    cls = something;
else 
    cls = something_else;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...