Можно ли выполнить вычисления перед super () в конструкторе? - PullRequest
61 голосов
/ 20 февраля 2010

Учитывая, что у меня есть класс Base, у которого есть конструктор с одним аргументом с объектом TextBox в качестве аргумента. Если у меня есть класс Simple следующей формы:

public class Simple extends Base {
  public Simple(){
    TextBox t = new TextBox();
    super(t);
    //wouldn't it be nice if I could do things with t down here?
  }
}

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

public class Simple extends Base {
  public Simple(){
    super(new TextBox());
  }
}

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

Есть ли способ обойти это ограничение? Есть ли хороший и безопасный способ хранения переменных для вещей, которые вы могли бы создать ДО вызова super, но ПОСЛЕ того, как вы ввели конструктор? Или, в более общем смысле, допускается выполнение вычислений до того, как супер будет вызван super, но внутри конструктора?

Спасибо.

Ответы [ 6 ]

69 голосов
/ 21 февраля 2010

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

public class Simple extends Base {
    private Simple(TextBox t) {
        super(t);
        // continue doing stuff with t here
    }

    public Simple() {
        this(new TextBox());
    }
}

Для более сложных вещей вы должны использовать фабричный или статический фабричный метод.

24 голосов
/ 21 июля 2013

У меня была такая же проблема с вычислениями перед супер вызовом. Иногда вы хотите проверить некоторые условия перед вызовом super(). Например, у вас есть класс, который использует много ресурсов при создании. подкласс хочет получить некоторые дополнительные данные и, возможно, захочет сначала проверить их, прежде чем вызывать супер-конструктор. Существует простой способ обойти эту проблему. может выглядеть немного странно, но работает хорошо:

Используйте закрытый статический метод внутри вашего класса, который возвращает аргумент супер-конструктора, и выполните ваши проверки внутри:

public class Simple extends Base {
  public Simple(){
    super(createTextBox());
  }

  private static TextBox createTextBox() {
    TextBox t = new TextBox();
    t.doSomething();
    // ... or more
    return t;
  }
}
8 голосов
/ 20 февраля 2010

Это требуется языком для обеспечения надежного построения суперкласса сначала . В частности, «Если конструктор не вызывает явно конструктор суперкласса, компилятор Java автоматически вставляет вызов в конструктор суперкласса без аргументов».

В вашем примере суперкласс может полагаться на состояние t во время построения. Вы всегда можете попросить копию позже.

Здесь обстоятельно обсуждается здесь и здесь .

0 голосов
/ 09 марта 2019

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

public class MyClass {

    private static Supplier<MyType> myTypeSupplier = () -> {
        return new MyType();
    };

    public MyClass() {
        super(clientConfig, myTypeSupplier.get());
    }
}
0 голосов
/ 20 февраля 2010

Вот как работает Java :-) Существуют технические причины, по которым он был выбран именно таким образом. Действительно, может показаться странным, что вы не можете выполнять вычисления на локальных компьютерах перед вызовом super, но в Java объект должен сначала быть выделен, и поэтому ему нужно пройти весь путь до Object, чтобы все поля были правильно инициализированы, прежде чем вы могли случайно модифицировать их.

В вашем случае в большинстве случаев есть геттер, который позволяет вам получить доступ к параметру, который вы дали super (). Так что вы бы использовали это:

super( new TextBox() );
final TextBox box = getWidget();
... do your thing...
0 голосов
/ 20 февраля 2010

Причина, по которой второй пример разрешен, но не первый, скорее всего, поддерживает язык в чистоте и не вводит странных правил.

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

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