Arhn - архитектура программирования

проблемы вызова переопределяемых методов из конструктора

если можно, поясните мне эту фразу

вот, автор сказал:

Не вызывайте переопределяемые методы из конструкторов. При создании объекта подкласса это может привести к вызову переопределенного метода до того, как объект подкласса будет полностью инициализирован. Вспомните, что когда вы конструируете объект подкласса, его конструктор сначала вызывает один из прямых конструкторов суперкласса. Если конструктор суперкласса вызывает переопределяемый метод, версия этого метода подкласса будет вызвана конструктором суперкласса — до того, как тело конструктора подкласса сможет выполниться.

я не могу понять, как можно вызвать версию переопределяемого метода подкласса в конструкторе суперкласса

TnX


Ответы:


1

Сначала вы должны провести различие между созданием экземпляра и инициализацией. Создание экземпляра — это процесс создания экземпляра типа (выделение для него пространства и получение ссылки на это пространство). Инициализация — это процесс установки состояния экземпляра в исходное значение.

Возьмем следующую иерархию типов:

class Foo { 
    public Foo() {}
}
class Bar extends Foo {
    public Bar() {super();}
}

Выражения создания нового экземпляра

new Bar();

вызывать инстанцирование и инициализацию. Инстанцирование происходит первым. Java создает экземпляр конкретного типа Bar.

Затем необходимо выполнить инициализацию. В иерархии наследования инициализация следует той же иерархии, но сверху вниз.

Object
   |
  Foo 
   |
  Bar

Сначала запускается конструктор для Object, чтобы инициализировать состояние, определенное как часть Object, затем запускается конструктор для Foo, чтобы инициализировать состояние, определенное как часть Foo, и, наконец, запускается конструктор для Bar, чтобы инициализировать состояние, определенное в Bar. Ваш экземпляр по-прежнему относится к типу Bar. Таким образом, полиморфизм по-прежнему применяется. Если вы вызываете метод экземпляра, и этот метод переопределяется где-то ниже в иерархии, будет вызвана эта реализация.

Именно об этом говорит эта цитата. Это опасно. Подробнее здесь:

Что не так с переопределяемыми вызовами методов в конструкторах?

29.05.2015

2

Чтобы проиллюстрировать, почему это плохая идея с некоторым (упрощенным) кодом, рассмотрим эти два класса:

class Greeter {
    protected Greeter() {
        printHello();
    }

    protected void printHello() {
        System.out.println("Hello");
    }
}

Выглядит достаточно просто, он печатает Hello всякий раз, когда вы его создаете. Теперь давайте расширим его:

class NamedGreeter extends Greeter {
    private String name;

    public NamedGreeter(String name) {
        this.name = name;
    }

    @Override
    protected void printHello() {
        System.out.println("Hello " + name);
    }
}

Намерение явно состоит в том, чтобы NamedGreeter приветствовал вас по имени при создании экземпляра, но на самом деле он всегда будет печатать Hello null, потому что суперконструктор вызывается первым, когда создается экземпляр NamedGreeter.

Благодаря тому, как работает полиморфизм, каждый раз, когда метод printHello() вызывается для NamedGreeter (даже если этот вызов исходит из класса Greeter), будет вызываться реализация в NamedGreeter. Вызов этого метода из конструктора родительского класса означает, что даже если дочерний класс расширит его, никакие поля, определенные дочерним классом, не будут инициализированы просто потому, что в дочернем конструкторе невозможно ничего сделать (например, инициализировать поля) до того, как вызывается родительский конструктор.

29.05.2015

3

Пример, демонстрирующий, что дочерний метод будет вызван:

class Foo {
  static class Parent {
    Parent() {
      someMethod();
    }
    void someMethod() {}
  }

  static class Child extends Parent {
    @Override void someMethod() {
      throw new AssertionError("Invoked");
    }
  }

  public static void main(String[] args) {
    new Child();  // Throws Exception.
  }
}

Вывод:

Exception in thread "main" java.lang.AssertionError: Invoked
        at Foo$Child.someMethod(Foo.java:16)
        at Foo$Parent.<init>(Foo.java:9)
        at Foo$Child.<init>(Foo.java:14)
        at Foo.main(Foo.java:21)
29.05.2015
Новые материалы

Коллекции публикаций по глубокому обучению
Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

Представляем: Pepita
Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

Советы по коду Laravel #2
1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

3 способа решить квадратное уравнение (3-й мой любимый) -
1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

Создание VR-миров с A-Frame
Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

Демистификация рекурсии
КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..