Полиморфный метод в конструкторе (Java) - PullRequest
11 голосов
/ 03 февраля 2012

Класс A вызывает открытый метод f() в конструкторе. Класс B переопределяет метод f() со своей собственной реализацией.

Предположим, вы создаете объект B .. метод f() объекта B будет вызван в конструкторе объекта A, хотя объект B не полностью инициализирован.

Кто-нибудь может объяснить это поведение?

РЕДАКТИРОВАТЬ: Да, это не рекомендуется практиковать ... пока я не понимаю почему Java не вызывает f() реализацию базы Класс A вместо "обращения" к реализации f() производного класса B.

Код:

class A {
    A() {
        System.out.println("A: constructor");
        f();
    }

    public void f() {
        System.out.println("A: f()");
    }
}

class B extends A {
    int x = 10;
    B() {
        System.out.println("B: constructor");
    }

    @Override
    public void f() {
        System.out.println("B: f()");
        this.x++;
        System.out.println("B: x = " + x);

    }
}

public class PolyMethodConst {
    public static void main(String[] args) {
        new B();
    }
}

Выход:

A: constructor
B: f()
B: x = 1
B: constructor

Ответы [ 4 ]

8 голосов
/ 03 февраля 2012

Вы правы, вот как это работает. Это не рекомендуемая практика, потому что тот, кто наследует ваш класс, может непреднамеренно нарушить его.

4 голосов
/ 03 февраля 2012

Каждый раз, когда вы создаете экземпляр подкласса, сначала вызывается конструктор суперклассов (неявный super()). Так что печатает

a: constructor

f() вызывается следующим, и поскольку подкласс переопределяет метод суперкласса, подкласс f() вызывается. Итак, вы увидите

B: f()

Теперь подкласс еще не инициализирован (все еще выполняется super ()) , поэтому x по умолчанию соответствует значению 0, поскольку это значение по умолчанию для типа int. Поскольку вы увеличиваете его (this.x++;), оно становится 1

B: x = 1

Теперь конструктор суперкласса завершен и возобновляет работу в конструкторе подклассов и, следовательно,

B: constructor

Переменные экземпляра теперь установлены на указанные вами значения (по сравнению со значениями по умолчанию, соответствующими типу (0 для чисел, false для boolean и null для ссылок))

ПРИМЕЧАНИЕ: Если вы теперь напечатаете значение x на вновь созданном объекте, оно будет 10

Поскольку это плохая практика, инструменты статического анализа кода (PMD, FIndBugs и т. Д.) Предупреждают вас, если вы пытаетесь это сделать.

0 голосов
/ 03 февраля 2012

Когда new B(), конструктор А вызывается неявно или вызывается через super(). Хотя он определен в классе A, фактически текущий класс - B.

Попробуйте добавить отладочную информацию ниже в конструктор и функции A.

System.out.println(this.getClass());

В вашем случае функция f () в классе A была переопределена классом B, поэтому функция в A () будет вызывать реализацию B (). Однако, если f () является закрытым методом и не может быть переопределен B, A.f () будет вызываться с более высокими приоритетами.

Но, как говорили другие, это не очень хорошая практика.

0 голосов
/ 03 февраля 2012

Я просто предоставлю ссылку, так как я не очень хорошо осведомлен по этому вопросу.См. здесь для учебника, в котором говорится о порядке вызова конструкторов.

Наиболее заметная цитата в конце страницы, относящаяся к описываемой вами ситуации, следующая:

  1. Вызывается конструктор базового класса.Этот шаг повторяется рекурсивно, так что сначала создается корень иерархии, а затем следующий производный класс и т. Д., Пока не будет достигнут самый производный класс.
  2. Инициализаторы-члены вызываются в порядкедекларация.Тело конструктора производного класса называется.

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

Но, как упоминал Билл, это не очень хорошая практика.Следуй тому, что говорит Билл.У него больше репутации, чем у меня.

РЕДАКТИРОВАТЬ : Более полный ответ см. в этом ответе Джона Скита .Ссылка в этом ответе не работает, и может быть найдена только PDF-копия JLS. AFAIK . Здесь - копия JLS в формате .pdf.Соответствующий раздел - Раздел 8.8.7.1.В этом ответе есть объяснение порядка вызова конструктора.

...