Виртуальные методы 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)?

Другие вопросы по тегам