Почему javac жалуется на не инициализированная переменная?

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

String var;
clazz.doSomething(var);

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

Переменный 'var', возможно, не был инициализирован

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

String var = null;

??

15
задан AlBlue 2 February 2010 в 21:03
поделиться

6 ответов

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

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

Реализует все необходимые конструкторы, деструктор, копирование/присвоение, своп, ядда-ядда. Вот вам:

#include <cassert>
#include <new>

template <typename T>
class lazy_object
{
public:
    // types
    typedef T value_type;
    typedef const T const_value_type;
    typedef value_type& reference;
    typedef const_value_type& const_reference;
    typedef value_type* pointer;
    typedef const_value_type* const_pointer;

    // creation
    lazy_object(void) :
    mObject(0),
    mBuffer(::operator new(sizeof(T)))
    {
    }

    lazy_object(const lazy_object& pRhs) :
    mObject(0),
    mBuffer(::operator new(sizeof(T)))
    {
        if (pRhs.exists())
        {
            mObject = new (buffer()) T(pRhs.get());
        }
    }

    lazy_object& operator=(lazy_object pRhs)
    {
        pRhs.swap(*this);

        return *this;
    }

    ~lazy_object(void)
    {
        destroy();
        ::operator delete(mBuffer);
    }

    // need to make multiple versions of this.
    // variadic templates/Boost.PreProccesor
    // would help immensely. For now, I give
    // two, but it's easy to make more.
    void create(void)
    {
        destroy();
        mObject = new (buffer()) T();
    }

    template <typename A1>
    void create(const A1 pA1)
    {
        destroy();
        mObject = new (buffer()) T(pA1);
    }

    void destroy(void)
    {
        if (exists())
        {
            mObject->~T();
            mObject = 0;
        }
    }

    void swap(lazy_object& pRhs)
    {
        std::swap(mObject, pRhs.mObject);
        std::swap(mBuffer, pRhs.mBuffer);
    }

    // access
    reference get(void)
    {
        return *get_ptr();
    }

    const_reference get(void) const
    {
        return *get_ptr();
    }

    pointer get_ptr(void)
    {
        assert(exists());
        return mObject;
    }

    const_pointer get_ptr(void) const
    {
        assert(exists());
        return mObject;
    }

    void* buffer(void)
    {
        return mBuffer;
    }

    // query
    const bool exists(void) const
    {
        return mObject != 0;
    }

private:
    // members
    pointer mObject;
    void* mBuffer;
};

// explicit swaps for generality
template <typename T>
void swap(lazy_object<T>& pLhs, lazy_object<T>& pRhs)
{
    pLhs.swap(pRhs);
}

// if the above code is in a namespace, don't put this in it!
// specializations in global namespace std are allowed.
namespace std
{
    template <typename T>
    void swap(lazy_object<T>& pLhs, lazy_object<T>& pRhs)
    {
        pLhs.swap(pRhs);
    }
}

// test use
#include <iostream>

int main(void)
{
    // basic usage
    lazy_object<int> i;
    i.create();
    i.get() = 5;

    std::cout << i.get() << std::endl;

    // asserts (not created yet)
    lazy_object<double> d;
    std::cout << d.get() << std::endl;
}

В вашем случае, просто создайте члена в вашем классе: leny _ object < B > и вы закончили. Нет ручных выпусков или создания конструкторов копий, деструкторов и т.д. Все берется в вашем хорошем, маленьком повторно используемом классе.:)

EDIT

Устранена потребность в векторе, должен сэкономить немного места, а что-нет.

EDIT 2

Для использования стека вместо кучи используется aligned _ storage и alignment _ of . Я использовал boost , но эта функциональность существует как в TR1, так и в C++ 0x. Мы теряем возможность копирования, а значит, и обмена.

#include <boost/type_traits/aligned_storage.hpp>
#include <cassert>
#include <new>

template <typename T>
class lazy_object_stack
{
public:
    // types
    typedef T value_type;
    typedef const T const_value_type;
    typedef value_type& reference;
    typedef const_value_type& const_reference;
    typedef value_type* pointer;
    typedef const_value_type* const_pointer;

    // creation
    lazy_object_stack(void) :
    mObject(0)
    {
    }

    ~lazy_object_stack(void)
    {
        destroy();
    }

    // need to make multiple versions of this.
    // variadic templates/Boost.PreProccesor
    // would help immensely. For now, I give
    // two, but it's easy to make more.
    void create(void)
    {
        destroy();
        mObject = new (buffer()) T();
    }

    template <typename A1>
    void create(const A1 pA1)
    {
        destroy();
        mObject = new (buffer()) T(pA1);
    }

    void destroy(void)
    {
        if (exists())
        {
            mObject->~T();
            mObject = 0;
        }
    }

    // access
    reference get(void)
    {
        return *get_ptr();
    }

    const_reference get(void) const
    {
        return *get_ptr();
    }

    pointer get_ptr(void)
    {
        assert(exists());
        return mObject;
    }

    const_pointer get_ptr(void) const
    {
        assert(exists());
        return mObject;
    }

    void* buffer(void)
    {
        return mBuffer.address();
    }

    // query
    const bool exists(void) const
    {
        return mObject != 0;
    }

private:
    // types
    typedef boost::aligned_storage<sizeof(T),
                boost::alignment_of<T>::value> storage_type;

    // members
    pointer mObject;
    storage_type mBuffer;

    // non-copyable
    lazy_object_stack(const lazy_object_stack& pRhs);
    lazy_object_stack& operator=(lazy_object_stack pRhs);
};

// test use
#include <iostream>

int main(void)
{
    // basic usage
    lazy_object_stack<int> i;
    i.create();
    i.get() = 5;

    std::cout << i.get() << std::endl;

    // asserts (not created yet)
    lazy_object_stack<double> d;
    std::cout << d.get() << std::endl;
}

И вот мы идем.

-121--2666421-

Вы устанавливаете идентификатор, но это идентификатор. Вы не должны устанавливать его, SQL делает это для вас.

Вместо создания нового сеанса используйте тот же самый сеанс и извлекайте его с помощью команды Get вместо Load.

О разнице между загрузкой и получением: http://ayende.com/Blog/archive/2009/04/30/nhibernate-ndash-the-difference-between-get-load-and-querying-by.aspx

-121--5086182-

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

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

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

  • При создании каждой переменной класса, переменной экземпляра или компонента массива инициализируется значение по умолчанию:
    • [вырезанный список всех значений по умолчанию]
  • Каждый параметр метода инициализируется соответствующим значением аргумента, предоставленным инициатором метода.
  • Каждый параметр конструктора инициализируется соответствующим значением аргумента, предоставленным выражением создания экземпляра класса или явным вызовом конструктора.
  • Параметр обработчика исключений инициализируется инициируемым объектом, представляющим исключение.
  • Локальной переменной должно быть явно присвоено значение, прежде чем она будет использована путем инициализации или назначения способом, который может быть проверен компилятором с помощью правил для определенного назначения.
23
ответ дан 1 December 2019 в 01:30
поделиться

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

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

int x;

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

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

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

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

7
ответ дан 1 December 2019 в 01:30
поделиться

Ditto на Билл К. Я добавляю:

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

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

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

PS Я видел много хромых программ, которые говорят такие вещи, как:

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

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

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

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

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

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

Но если вы инициализируете Foo в фиктивное значение, вроде

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

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

4
ответ дан 1 December 2019 в 01:30
поделиться

Существует новый, открытый исходный код Основной график .

-121--4213277-

То, что вы просите, невозможно найти в одной книге. Многое из того, что вы упомянули, лучше всего найти в руководствах пользователя или функциональных спецификациях для различных процессоров. Я рекомендую начать с понимания ядра x86 арки и работать оттуда. Одно из старых руководств Intel 386 или 486 может быть хорошим началом.

Я не знаю веб-сайтов для такого типа информации.

-121--5085892-

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

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

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

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

List<Thing> somethings = new List(); 

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

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

1
ответ дан 1 December 2019 в 01:30
поделиться

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

MyClass cls;
if (condition) {
    cls = something;
else 
    cls = something_else;
0
ответ дан 1 December 2019 в 01:30
поделиться

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

Bill IV

0
ответ дан 1 December 2019 в 01:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: