Вызов переопределенного метода из конструктора суперкласса просто не будет работать - не делайте этого. Конструктор суперкласса всегда должен заканчиваться раньше, чем у подкласса. Во время выполнения конструктора суперкласса рассматриваемый объект является (частично инициализированным) экземпляром суперкласса, а не подклассом! Поэтому, если вы попытаетесь вызвать какую-либо переопределенную функцию из конструктора, поля подкласса, от которых она может зависеть, еще не инициализированы (как вы заметили). Это фундаментальный факт проектирования класса, и обходного пути нет.
Как объяснено в Effective Java 2nd. Ред. (глава 4, пункт 17):
Существуют [...] ограничения, которым должен подчиняться класс, чтобы
наследование. Конструкторы не должны вызывать переопределяемые методы напрямую
или косвенно. Если вы нарушите это правило, произойдет сбой программы.
Конструктор суперкласса выполняется перед конструктором подкласса, поэтому
переопределяющий метод в подклассе будет вызван перед подклассом
конструктор побежал. Если переопределяющий метод зависит от какой-либо инициализации
выполняется конструктором подкласса, метод не будет вести себя как
ожидается.
Если вы можете изменить реализацию суперкласса, попробуйте переместить вызов виртуальной функции из конструктора. Одним из способов достижения этого является использование фабричного метода:
class Super {
public void init() { ... }
}
class Subclass extends Super {
private Subclass() { super(); ... }
public void init() { super.init(); ... }
public static Subclass createInstance() {
Subclass instance = new Subclass();
instance.init();
return instance;
}
}
Обратите внимание, что конструктор Subclass
является закрытым, чтобы гарантировать, что он может быть создан только через createInstance()
, таким образом, экземпляры всегда инициализируются должным образом. OTOH это также предотвращает дальнейшее создание подклассов. Тем не менее, подклассы конкретного класса в любом случае не рекомендуется - класс, предназначенный для подкласса, должен быть абстрактным (с конструктором protected
в данном случае). И, конечно же, любые последующие подклассы также должны иметь непубличные конструкторы и статические фабричные методы, которые старательно вызывают init()
...