Вопрос-ответ

Should I instantiate instance variables on declaration or in the constructor?

Должен ли я создавать экземпляры переменных экземпляра при объявлении или в конструкторе?

Есть ли какие-либо преимущества у любого подхода?

Пример 1:

class A {
B b = new B();
}

Пример 2:

class A {
B b;

A() {
b = new B();
}
}
Переведено автоматически
Ответ 1

  • Разницы нет - инициализация переменной экземпляра фактически помещается компилятором в конструктор (ы).

  • Первый вариант более удобочитаем.

  • У вас не может быть обработки исключений с первым вариантом.

  • Дополнительно существует блок инициализации, который также помещается компилятором в конструктор (ы):


    {
    a = new A();
    }

Проверьте объяснение и рекомендации Sun

Из этого руководства:


Объявления полей, однако, не являются частью какого-либо метода, поэтому они не могут выполняться как инструкции. Вместо этого компилятор Java автоматически генерирует код инициализации поля экземпляра и помещает его в конструктор или конструкторы для класса. Код инициализации вставляется в конструктор в том порядке, в каком он отображается в исходном коде, что означает, что инициализатор поля может использовать начальные значения полей, объявленных перед ним.


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

ExpensiveObject o;

public ExpensiveObject getExpensiveObject() {
if (o == null) {
o = new ExpensiveObject();
}
return o;
}

И, в конечном счете (как указал Билл), ради управления зависимостями лучше избегать использования new оператора в любом месте вашего класса. Вместо этого предпочтительнее использовать внедрение зависимостей, т. Е. Позволить кому-то другому (другому классу / фреймворку) создавать экземпляры и внедрять зависимости в ваш класс.

Ответ 2

Другим вариантом было бы использовать внедрение зависимостей.

class A{
B b;

A(B b) {
this.b = b;
}
}

Это снимает ответственность за создание B объекта из конструктора A. Это сделает ваш код более тестируемым и более простым в обслуживании в долгосрочной перспективе. Идея состоит в том, чтобы уменьшить связь между двумя классами A и B. Преимущество, которое это дает вам, заключается в том, что теперь вы можете передать любой объект, который расширяет B (или реализует B, если это интерфейс), в Aконструктор, и он будет работать. Одним из недостатков является то, что вы отказываетесь от инкапсуляции B объекта, поэтому он предоставляется вызывающей стороне A конструктора. Вам придется подумать, стоят ли преимущества такого компромисса, но во многих случаях так оно и есть.

Ответ 3

Сегодня я обжегся интересным способом:

class MyClass extends FooClass {
String a = null;

public MyClass() {
super(); // Superclass calls init();
}

@Override
protected void init() {
super.init();
if (something)
a = getStringYadaYada();
}
}

Видите ошибку? Оказывается, что a = null инициализатор вызывается после вызова конструктора суперкласса. Поскольку конструктор суперкласса вызывает init(), за инициализацией a следует a = null инициализация.

Ответ 4

мое личное "правило" (которое почти никогда не нарушается) заключается в том, чтобы:


  • объявляйте все переменные в начале блока

  • сделайте все переменные окончательными, если они не могут быть

  • объявляйте по одной переменной в строке

  • никогда не инициализируйте переменную там, где она объявлена

  • инициализируйте что-либо в конструкторе только тогда, когда для выполнения инициализации требуются данные из конструктора

Итак, у меня был бы код типа:

public class X
{
public static final int USED_AS_A_CASE_LABEL = 1; // only exception - the compiler makes me
private static final int A;
private final int b;
private int c;

static
{
A = 42;
}

{
b = 7;
}

public X(final int val)
{
c = val;
}

public void foo(final boolean f)
{
final int d;
final int e;

d = 7;

// I will eat my own eyes before using ?: - personal taste.
if(f)
{
e = 1;
}
else
{
e = 2;
}
}
}

Таким образом, я всегда на 100% уверен, где искать объявления переменных (в начале блока) и их назначения (как только это имеет смысл после объявления). Это также потенциально оказывается более эффективным, поскольку вы никогда не инициализируете переменную значением, которое не используется (например, объявляете и инициализируете переменные, а затем создаете исключение до того, как половина этих переменных должна иметь значение). Вы также не выполняете бессмысленную инициализацию (например, int i = 0; а затем позже, перед использованием "i", делайте i = 5;.

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

Ваш пробег может отличаться.

java