Вызов метода из конструктора
Извините за любые незначительные синтаксические ошибки или что-то еще, я испытываю это с помощью модуля Jitsi и не очень хорошо разбираюсь в Java, хочу подтвердить, что происходит, и почему и как его следует исправлять.
public abstract class A
{
public A()
{
this.load();
}
protected void load()
{
}
}
public class B extends A
{
private String testString = null;
public B()
{
super();
}
@Override
protected void load()
{
testString = "test";
}
}
Приложение выполняет это при создании экземпляра класса B с использованием класса нагрузки по методу name:
- Вызывает переопределенную нагрузку() в классе B
- Инициализирует переменные (вызывает "private string testString = null" в соответствии с отладчиком), обнуляя их.
Является ли это ожидаемым поведением Java? Что может быть причиной этого? Это приложение Java 1.6, работающее на 1,7 JDK.
Ответы
Ответ 1
Является ли это ожидаемым поведением Java?
Да.
Что может быть причиной этого?
Вызов неконфигурированного переопределенного метода в конструкторе внеклассного суперкласса.
Посмотрите, что происходит шаг за шагом:
- Создается экземпляр
B
.
-
B()
вызывает конструктор суперкласса - A()
, чтобы инициализировать члены суперкласса.
-
A()
теперь вызывает не конечный метод, который переопределяется в классе B
, как часть инициализации.
- Поскольку экземпляр в контексте имеет класс
B
, вызываемый метод load()
имеет класс B
.
-
load()
инициализирует поле экземпляра класса B
- testString
.
- Конструктор суперкласса завершает работу и возвращает (предположив, что цепочка конструктора до класса
Object
завершена)
- Конструктор
B()
начинает выполнение, инициализируя его собственный член.
- Теперь, в рамках процесса инициализации,
B
перезаписывает предыдущее записанное значение в testString
и повторно инициализирует его до null
.
Мораль: никогда не вызывать неконкурентный публичный метод не конечного класса в нем конструктор.
Ответ 2
Это общий шаблон проблемы с инициализацией на этапе строительства и часто можно найти в коде инфраструктуры и самодельных DAO.
Назначение "null" не требуется и может быть удалено.
Если этого недостаточно, как быстрый патч, то: Переместите все post-construction init в отдельный метод и оберните все это в псевдо-конструктор "статический метод".
И если вы делаете вещи DAO, очень хорошо различать "load" и "create", поскольку это совершенно разные экземпляры. Определите отдельные методы "статического конструктора" и, возможно, отдельные внутренние элементы для них.
abstract public class A {
protected void initAfterCreate() {}
}
public class B {
@Override
protected void initAfterCreate() {
this.testString = "test";
}
// static constructors;
// --
static public B createB() {
B result = new B();
result.initAfterCreate();
}
}
Демонстрация разделения load/create для DAO:
public class Order {
protected int id;
protected boolean dbExists;
static public load (int id) {
Order result = new Order( id, true);
// populate from SQL query..
return result;
}
static public create() {
// allocate a key.
int id = KeyAlloc.allocate( "Order");
Order result = new Order( id, false);
}
// internal constructor; not for external access.
//
protected Order (int id, boolean dbExists) {
this.id = id;
this.dbExists = dbExists;
}
}