29

В 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; 
    } 
} 

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

+1

Вы пытаетесь предварительно инициализировать поле 'a'? – Woot4Moo

+1

Я не думаю, что вы можете это сделать. Любая инициализация, выполняемая вами в классе (даже если она находится вне конструктора), перемещается в каждый конструктор после вызова 'super'. Таким образом, супер-конструктор всегда запускается до инициализации поля. –

+5

@FredOverflow с 'a' доступен только в' Derived', почему имеет значение, что он инициализируется * до того, как вызывается '' super() '? Инициализация его сразу же после того, как не имеет значения в примере вашего обеспечения (если вы не вызываете метод overriden из базового конструктора, который начинает пахнуть довольно подозрительно). – assylias

ответ

20

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

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

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

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

, вы уверены в этом? Я натолкнулся на это: _...Это может быть так, что B видит эти события в следующем порядке (и компилятор также может свободно изменять порядок инструкций, подобных этому): выделить память, назначить ссылку на ресурс, конструктор вызовов. Предположим, что поток B входит после выделения памяти и задано поле ресурса, но до того, как конструктор будет вызываться .._ из @BrianGoetz в статье DCL, поэтому компилятор, по-видимому, может сделать некоторые оптимизации, которые также включают в себя назначение ссылки и вызова после этого конструктор! – mkilic

1

Это запрещено Java language specification (section 8.8.7):

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

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

ConstructorBody:

{ ExplicitConstructorInvocationopt BlockStatementsopt } 
4

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

Но есть обходные пути. Один из них заключается в создании фабричного класса, который получает значение и передает его конструктору класса 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; 
    } 
    ... 
} 
10

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

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(); 
    } 
} 

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

+0

Я не думаю, что это работает на Java, и не без оснований. Ищем ссылку ... –

+0

Это работает, проверено ... –

+1

+1 - Ты прав. Ик. Я думал о C++. Тем не менее - я не думаю, что взлом необходим - см. Мой ответ. –

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) 
    } 

    ... 
}