2012-06-29 8 views
2

У меня есть пара классов, похожих на это;Может ли частное конечное поле подкласса java быть инициализировано до завершения суперконструктора?

public abstract class Class1 { 

//... 

    public Class1() { 
     //... 
     function2(); 
     //... 
    } 

    protected abstract void function2(); 

} 

public class Class2 implements Class1 { 

    private final OnSomethingListener mOnSomethingListener = new OnSomethingListener() { 
     @Override 
     onSomething() { 
      doThatOtherThing(); 
     } 
    } 

    protected void function2() { 
     //uses mOnSomethingListener 
     //however mOnSomethingListener is null when this function is called from super() 
     //... 
    } 

    public Class2() { 
     super(); 
    } 
} 

Я предполагаю, что слушатель является пустым, потому что я эффективно ссылаться на него из super() и это еще не экземпляр. Однако, я хочу сделать это final, потому что, ну, это так. Могу ли я получить это поле (слушатель) для инициализации во времени, не помещая его в суперкласс (который не будет использовать слушателя, когда-либо)?

+1

Правильное поведение должно превзойти «используя« финал », где это возможно». Я бы просто использовал getter для слушателя, который лениво инициализирует поле. Это делает порядок инициализации совершенно ясным и явным, не полагаясь на техничность. В качестве альтернативы вы могли бы запустить вторую функцию инициализации после конструкторов и вызвать 'function2()', но я все же не думаю, что это лучший выбор, чем удаление «final». – millimoose

+2

Суперклас будет инициализирован перед тем, как один из его подклассов. –

+0

http://stackoverflow.com/questions/11104339/create-an-object-before-the-super-call-in-java –

ответ

3

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

Порядок звонков формально определен в section 8.8.7.1 of the JLS. Обобщая соответствующие последние части (где S является суперкласс и C является подкласс):

После определения немедленно заключающую экземпляр I по отношению к S (если таковые имеются), оценка суперкласса продолжается заявление вызова конструктора с помощью оценивая аргументы конструктору слева направо, как при обычном вызове метода; а затем вызывает конструктор.

Наконец, если инструкция вызова конструктора суперкласса выполняется нормально, то выполняются все инициализаторы переменной экземпляра C и все инициализаторы экземпляров C. Если экземпляр инициализатор или экземпляр инициализатор переменного I текстуально предшествует другой экземпляр инициализатор или экземпляр переменного инициализатор J, то я выполняется перед J.

Таким образом, когда работает конструктор суперкласса, подкласс и весь его поле полностью неинициализированные. По этой причине неправильная практика вызывать переопределенные методы из конструктора. Вы, по сути, даете ссылку на объект «побег» из своего конструктора, что означает, что все гарантии построения отключены (включая такие вещи, как конечные поля, изменяющие значение и т. Д.).

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

Например, можно ожидать там быть разница между:

protected String function2() { 
    return "foo"; 
} 

и

private final String foo = "foo"; 

protected String function2() { 
    return foo; 
} 

проблемы Анализ, как это трудно, потому что они нарушают ментальную модель того, как работают классы. Лучше всего избегать этой ситуации.

2

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

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

5

Ваш дизайн представляет собой экземпляр «просочившейся проблемы this» и является анти-шаблоном на Java. Вы никогда не должны вызывать общедоступный метод от конструктора.

+0

Как это не сделать :) –

+0

Типичным способом вокруг него будет двухэтапная процедура, в которой вы сначала создаете экземпляр, а затем инициализируете. –

+0

Это, как правило, проблема, если вы работаете с чем-то вроде Wicket, который живет при выполнении всей инициализации объекта из конструктора. (Чувство, которое я могу понять, использование отдельных инициализаторов кажется неестественным.) – millimoose