Почему javac жалуется на не инициализированную переменную?
Для этого кода Java:
String var;
clazz.doSomething(var);
Почему компилятор сообщает об этой ошибке:
Возможно, переменная 'var' не была инициализирована
Я думал, что все переменные или ссылки были инициализированы на null
. Зачем вам нужно:
String var = null;
??
Ответы
Ответ 1
Инстанции и переменные класса инициализируются нулем (или 0), но локальные переменные не являются.
См. §4.12.5 JLS за очень подробное объяснение, в котором говорится в основном одно и то же:
Каждая переменная в программе должна иметь значение перед его значением:
- Каждая переменная класса, переменная экземпляра или компонент массива инициализируется значением по умолчанию при его создании:
- [список всех значений по умолчанию]
- Каждый параметр метода инициализируется значением соответствующего аргумента, предоставленным вызовом метода.
- Каждый параметр конструктора инициализируется значением соответствующего аргумента, предоставленным выражением создания экземпляра класса или явным вызовом конструктора.
- Параметр исключения-обработчика инициализируется для брошенного объекта, представляющего исключение.
- Локальная переменная должна быть явно задана значением до ее использования путем инициализации или назначения таким образом, который может быть проверен компилятором с использованием правил для определенного назначения.
Ответ 2
Это потому, что Java очень помогает (насколько это возможно).
Он будет использовать эту же логику, чтобы поймать некоторые очень интересные крайности, которые вы, возможно, пропустили. Например:
int x;
if(cond2)
x=2;
else if(cond3)
x=3;
System.out.println("X was:"+x);
Это не удастся, потому что был другой случай, который не был указан. Дело в том, что в этом случае совершенно необходимо указать конкретный случай, даже если это просто ошибка (То же самое относится к по умолчанию: условие в инструкции switch).
То, что вам следует убирать из этого, интересно, не инициализирует локальные переменные до тех пор, пока вы не выясните, что вам действительно нужно это делать. Если у вас есть привычка всегда говорить "int x = 0;" вы предотвратите функционирование этого фантастического детектора "плохой логики". Эта ошибка сэкономила мне больше одного раза.
Ответ 3
То же самое на Билл К. Добавляю:
Компилятор Java может защитить вас от вреда себе, если вы не установите переменную перед ее использованием внутри функции. Таким образом, он явно НЕ устанавливает значение по умолчанию, как описывает Билл К.
Но когда дело доходит до переменных класса, компилятору будет очень сложно сделать это за вас. Переменная класса может быть задана любой функцией класса. Компилятору было бы очень сложно определить все возможные порядки, в которых можно было бы вызвать функции. По крайней мере, он должен был бы проанализировать все классы в системе, которые вызывают любую функцию в этом классе. Возможно, вам придется изучить содержимое любых файлов данных или базы данных и как-то предсказать, какие данные будут делать пользователи. В лучшем случае задача будет чрезвычайно сложной, в худшем случае невозможной. Поэтому для переменных класса имеет смысл обеспечить надежный дефолт. Это по умолчанию, в основном, заполняет поле битами нуля, поэтому вы получаете нуль для ссылок, нуль для целых чисел, false для булевых и т.д.
Как говорит Билл, вы, безусловно, НЕ должны привыкать к автоматической инициализации переменных при их объявлении. Только инициализировать переменные во время объявления, если это действительно имеет смысл в контексте вашей программы. Например, если в 99% случаев вы хотите, чтобы x было равным 42, но внутри некоторого условия IF вы могли обнаружить, что это особый случай, а x должен быть 666, тогда отлично, начните с "int x = 42;" и внутри ПЧ переопределяют это. Но в более нормальном случае, когда вы вычисляете значение, основанное на любых условиях, не инициализируйте произвольное число. Просто заполните его расчетным значением. Затем, если вы делаете логическую ошибку и не можете установить значение в некоторой комбинации условий, компилятор может сказать вам, что вы испорчены, а не пользователь.
PS Я видел много хромых программ, которые говорят такие вещи, как:
HashMap myMap=new HashMap();
myMap=getBunchOfData();
Зачем создавать объект для инициализации переменной, когда вы знаете, что вы быстро выбросите этот объект за миллисекунду позже? Это просто пустая трата времени.
Edit
Чтобы принять тривиальный пример, предположим, что вы написали это:
int foo;
if (bar<0)
foo=1;
else if (bar>0)
foo=2;
processSomething(foo);
Это вызовет ошибку во время компиляции, потому что компилятор заметит, что при баре == 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, тогда это нормально. Но если на самом деле произошло то, что вы имели в виду один из тестов, которые должны быть <= или > =, или вы хотели включить окончательное дополнение для бара == 0, тогда вы обманули компилятор, чтобы не обнаружить вашу ошибку. И, кстати, таким образом, я думаю, что такая конструкция - плохой стиль кодирования: компилятор не только не может быть уверен, что вы намеревались, но и не может быть программистом будущего обслуживания.
Ответ 4
Мне нравится, что Bill K говорит о том, чтобы позволить компилятору работать для вас - я впал в инициализацию каждой автоматической переменной, потому что она казалась чем-то вроде Java. Я не понимал, что переменные класса (т.е. Постоянные вещи, о которых беспокоят конструкторы) и автоматические переменные (некоторые счетчики и т.д.) Различны, даже если EVERYTHING - это класс в Java.
Итак, я вернулся и удалил инициализацию, которую я использовал бы, например
List <Thing> somethings = new List<Thing>();
somethings.add(somethingElse); // <--- this is completely unnecessary
Ницца. Я получил предупреждение компилятора для
List<Thing> somethings = new List();
и я думал, что проблема заключается в отсутствии инициализации. НЕПРАВИЛЬНО. Проблема заключалась в том, что я не понимал правил, и мне нужен <Thing>
, указанный в "новом", а не какие-либо фактические элементы типа <Thing>
.
(Далее мне нужно узнать, как помещать буквально меньше и больше, чем знаки в HTML!)
Ответ 5
Я не знаю логики, но локальные переменные не инициализируются null
. Я думаю, сделать вашу жизнь легкой. Они могли бы сделать это с помощью переменных класса, если это было возможно. Это не значит, что вы должны сначала инициализировать его. Это нормально:
MyClass cls;
if (condition) {
cls = something;
else
cls = something_else;
Ответ 6
Конечно, если у вас действительно есть две строки поверх друг друга, когда вы показываете - объявляете ее, заполните ее, не требуя конструктора по умолчанию. Но, например, если вы хотите объявить что-то один раз и использовать его несколько или несколько раз, конструктор по умолчанию или нулевое объявление имеет значение. Или указатель на объект настолько легкий, что его лучше выделять его снова и снова внутри цикла, потому что выделение указателя намного меньше, чем создание объекта? (Предположительно, есть действительная причина для нового объекта на каждом шаге цикла).
Билль IV