Виртуальные методы Java: функция или ошибка?
Возьмите этот базовый класс:
public abstract class XMPPSubservice
{
protected XMPPService mTheService;
protected XMPPSubservice(Context context)
{
Intent intent = new Intent(context, XMPPService.class);
context.startService(intent);
}
public void onServiceInstance(XMPPService service) {
// TODO Auto-generated method stub
mTheService = service;
}
}
И этот производный класс:
public class PublicDataSubservice extends XMPPSubservice
{
private final SomeObject mObj = new SomeObject();
public PublicDataSubservice(Context context) {
super(context);
}
@Override
public void onServiceInstance(XMPPService service)
{
super.onServiceInstance(service);
mObj.doSomethingWith(mTheService);
}
}
Цель состояла в том, чтобы вызвать только mObj.doSomethingWith(mTheService); после того, как mTheService вступил в силу (что произошло в базовом классе). Дело в том, что он всегда выплевывал NPE на линии mObj. Я могу понять, почему это произошло, но для меня это выглядит странно. Так это ошибка или особенность DVM? Как насчет JVM?
2 ответа
Это совершенно правильно, и может произойти и в "ванильной" Java.
Инициализаторы переменных экземпляра выполняются только в начале тела конструктора после того, как конструктор суперкласса завершил выполнение. Так что пока XMPPSubservice
конструктор выполняется, mObj
является нулевым - вы затем вызываете виртуальный метод из конструктора, и переопределение в PublicDataService
выполняет.
Мораль: не вызывайте виртуальные методы из конструкторов, если только вам это не нужно, и в этом случае вы должны тщательно их документировать. (Очень редко это полезно, но вы должны изо всех сил избегать этого.) По сути, это означает, что вы в конечном итоге вызываете потенциально частично инициализированный объект, что и происходит здесь.
Я попробовал следующее с использованием реализаций заглушек ваших объектов в Java VM.
public static void main(String[] args) {
Context context = new Context();
PublicDataSubservice pds = new PublicDataSubservice(context);
XMPPService service = new XMPPService();
pds.onServiceInstance(service);
}
нет NullPointerException
,
Я что-то пропустил? я думаю что onServiceInstance
на самом деле должен быть вызван в результате context.getService(intent)
?