Подтвердить что ты не робот

Инициализировать поле перед запуском суперконструктора?

В Java есть ли способ инициализировать поле до запуска суперструктора?

Даже самые уродливые хаки, которые я могу придумать, отвергаются компилятором:

class Base
{
    Base(String someParameter)
    {
        System.out.println(this);
    }
}

class Derived extends Base
{
    private final int a;

    Derived(String someParameter)
    {
        super(hack(someParameter, a = getValueFromDataBase()));
    }

    private static String hack(String returnValue, int ignored)
    {
        return returnValue;
    }

    public String toString()
    {
        return "a has value " + a;
    }
}

Примечание. Проблема исчезла, когда я переключился с наследования на делегирование, но мне все равно хотелось бы знать.

4b9b3361

Ответ 1

Нет, нет никакого способа сделать это.

В соответствии с спецификациями языка переменные экземпляра даже не инициализируются до тех пор, пока не будет выполнен вызов super().

Это шаги, выполняемые во время шага конструктора создания экземпляра класса, взятого из ссылки:

  • Назначьте аргументы для конструктора вновь созданному параметру переменные для этого вызова конструктора.
  • Если этот конструктор начинается с явного вызова конструктора (§8.8.7.1) другого конструктора того же класса (используя это), затем оцените аргументы и обработайте вызов конструктора рекурсивно используя эти пять шагов. Если этот конструктор вызов завершается внезапно, то эта процедура завершается внезапно по той же причине; в противном случае, перейдите к шагу 5.
  • Этот конструктор не начинается с явного конструктора вызов другого конструктора в том же классе (с использованием этого). Если этот конструктор предназначен для класса, отличного от Object, тогда это конструктор начнется с явного или неявного вызова конструктор суперкласса (с использованием супер). Оцените аргументы и обрабатывать вызов конструктора суперкласса рекурсивно, используя эти пять шагов. Если этот вызов конструктора завершен внезапно, эта процедура завершается внезапно для того же причина. В противном случае перейдите к шагу 4.
  • Выполнить инициализаторы экземпляра и инициализаторы переменных экземпляра для этого класса, присваивая значения переменной экземпляра инициализаторы к соответствующим переменным экземпляра, в слева направо, в котором они отображаются в текстовом виде в источнике код для класса. Если выполнение любого из этих инициализаторов приводит к исключению, тогда никакие инициализаторы не обрабатываются и эта процедура завершается внезапно с тем же исключением. В противном случае перейдите к шагу 5.
  • Выполните оставшуюся часть тела этого конструктора. Если это выполнение завершается резко, то эта процедура завершается внезапно для та же самая причина. В противном случае эта процедура выполняется нормально.

Ответ 2

Супер конструктор будет работать в любом случае, но поскольку мы говорим о "уродливых хаках", мы можем воспользоваться этим

public class Base {
    public Base() {
        init();
    }

    public Base(String s) {
    }

    public void init() {
    //this is the ugly part that will be overriden
    }
}

class Derived extends Base{

    @Override
    public void init(){
        a = getValueFromDataBase();
    }
} 

Я никогда не предлагаю использовать такие хаки.

Ответ 3

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

Но есть обходные пути. Один из них заключается в создании класса factory, который получает значение и передает его в конструктор класса Derived.

class DerivedFactory {
    Derived makeDerived( String someParameter ) {
        int a = getValueFromDataBase();
        return new Derived( someParameter, a );
    }
}


class Derived extends Base
{
    private final int a;

    Derived(String someParameter, int a0 ) {
        super(hack(someParameter, a0));
        a = a0;
    }
    ...
}

Ответ 4

У меня есть способ сделать это.

class Derived extends Base
{
    private final int a;

    // make this method private
    private Derived(String someParameter,
                    int tmpVar /*add an addtional parameter*/) {
        // use it as a temprorary variable
        super(hack(someParameter, tmpVar = getValueFromDataBase()));
        // assign it to field a
        a = tmpVar;
    }

    // show user a clean constructor
    Derived(String someParameter)
    {   
        this(someParameter, 0)
    }

    ...
}

Ответ 5

Он запрещен спецификацией языка Java (раздел 8.8.7):

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

Тело конструктора должно выглядеть следующим образом:

ConstructorBody:

{ ExplicitConstructorInvocationopt BlockStatementsopt }