Должен ли я сохранять переменные экземпляра в Java всегда инициализированными или нет? - PullRequest
5 голосов
/ 13 февраля 2009

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

public class ItemManager {

  ItemMaster itemMaster;
  List<ItemComponentManager> components;

  ItemManager() {
    itemMaster = new ItemMaster();
    components = new ArrayList<ItemComponentManager>();
  }

  ...
}

Смысл в основном состоит в том, чтобы избежать утомительной проверки на null перед использованием переменной экземпляра где-нибудь в коде. Пока что это работает хорошо, и вам, в основном, не нужно значение null , так как вы также можете проверить наличие пустой строки или пустого списка и т. Д. Я не использую этот подход для переменных в области метода как область действия очень ограничена и поэтому не влияет на другие части кода.

Это все вроде эксперимента, поэтому я хотел бы знать, может ли этот подход сработать или есть какие-то подводные камни, которых я пока не вижу. Как правило, рекомендуется инициализировать переменные экземпляра?

Ответы [ 12 ]

9 голосов
/ 13 февраля 2009

Обычно я рассматриваю пустую коллекцию и нулевую коллекцию как две отдельные вещи:

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

Так что я действительно не думаю, что это или / или. И я бы объявил переменную final , если бы я инициализировал их в конструкторе. Если вы объявите его окончательным, читателю станет ясно, что эта коллекция не может быть нулевой.

4 голосов
/ 13 февраля 2009

Прежде всего, все неконечные переменные экземпляра должны быть объявлены private , если вы хотите сохранить контроль!

Рассмотрим также ленивую реализацию - это также позволяет избежать «плохого состояния», но инициализируется только при использовании:

class Foo {
    private List<X> stuff;
    public void add(X x) {
        if (stuff == null)
            stuff = new ArrayList<X>();
        stuff.add(x);
    }
    public List<X> getStuff() {
        if (stuff == null)
            return Collections.emptyList();
        return Collections.unmodifiableList(stuff);
    }
}

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

Подумайте, сколько экземпляров рассматриваемого объекта будет создано. Если их много, и вы всегда создаете списки (и в конечном итоге можете получить много пустых списков), вы можете создавать гораздо больше объектов, чем вам нужно.

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

Если вы работаете с DI / IOC, вы хотите, чтобы фреймворк выполнил эту работу за вас (хотя вы могли бы сделать это с помощью инжектора конструктора; я предпочитаю сеттеры) - Скотт

3 голосов
/ 13 февраля 2009

Я бы сказал, что это совершенно нормально, если вы помните, что у вас есть "пустые" значения заполнителей, а не реальные данные.

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

И просто прокомментируйте защитное кодирование. Если вы создаете объекты и никогда не устанавливаете их как нулевые, нет необходимости каждый раз проверять нулевое значение. Если по какой-либо причине вы получаете нулевое значение, то вы знаете, что что-то катастрофически пошло не так, и программа все равно должна аварийно завершить работу.

2 голосов
/ 13 февраля 2009

То, как я работаю с любой переменной, которую я объявляю, - это решить, изменится ли она в течение срока службы объекта (или класса, если он статический). Если ответ «нет», я делаю это окончательно.

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

  private final ItemMaster itemMaster;
  private final List components;

  // instance initialization block - happens at construction time
  {
      itemMaster = new ItemMaster();
      components = new ArrayList();
  }

То, как ваш код прямо сейчас, вы должны постоянно проверять на null, потому что вы не пометили переменные как private (что означает, что любой класс в том же пакете может изменить значения на null).

2 голосов
/ 13 февраля 2009

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

На самом деле я бы использовал следующий код вместо вашего:

public class ItemManager {

  ItemMaster itemMaster = new ItemMaster();
  List<ItemComponentManager> components = new ArrayList<ItemComponentManager>();

  ItemManager() {
     ...
  }
  ...
}
2 голосов
/ 13 февраля 2009

Я бы сделал их окончательными , если это возможно. Затем они должны быть инициализированы в конструкторе и не могут стать нулевыми.

Вы также должны сделать их закрытыми в любом случае, чтобы другие классы не могли присвоить им значение null. Если вы можете проверить, что null никогда не назначается в вашем классе, тогда подход будет работать.

1 голос
/ 13 февраля 2009

Объект должен быть на 100% готов к использованию после его создания. Пользователи не должны проверять наличие нулей. Оборонительное программирование - это путь, который нужно - держать чеки.

В интересах DRY вы можете поместить проверки в установщики и просто заставить конструктор вызвать их. Таким образом, вы не будете кодировать чеки дважды.

1 голос
/ 13 февраля 2009

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

Вы все еще должны проверить на ноль. Сторонние библиотеки и даже API Java иногда возвращают ноль.

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

1 голос
/ 13 февраля 2009

Да, очень хорошая идея инициализировать все переменные класса в конструкторе.

0 голосов
/ 13 февраля 2009

Из названия класса "ItemManager" ItemManager звучит как одиночка в некоторых приложениях. Если это так, вы должны исследовать и действительно, действительно, знать инъекцию зависимости. Используйте что-то вроде Spring (http://www.springsource.org/), чтобы создать и внедрить список ItemComponentManager в ItemManager.

Без DI инициализация вручную в серьезных приложениях - это кошмар для отладки, и подключение различных классов «менеджера» для выполнения узких тестов - ад.

Всегда используйте DI (даже при построении тестов). Для объектов данных создайте метод get (), который создает список, если он не существует. Однако, если объект сложный, вы почти наверняка найдете свою жизнь лучше, используя шаблон Factory или Builder, и F / B установит переменные-члены по мере необходимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...