Принудительный вызов метода базового класса - PullRequest
2 голосов
/ 24 апреля 2009

Следующий код при запуске явно печатает «B1 / A2 / B2». Теперь можно ли вместо этого напечатать «A1 / A2 / B2» (то есть # method2 () должен вызывать method1 () для A, а не для B)?

Примечание: мне не нужно проходить полиморфизм, этот вопрос только из любопытства.

class A {
    public void method1() {
        System.out.println("A1");
    }

    public void method2() {
        method1();
        System.out.println("A2");
    }
}

class B extends A {
    @Override public void method2() {
        super.method2();
        System.out.println("B2");

    }

    @Override public void method1() {
        System.out.println("B1");
    }
}

public class Tmp {
    public static void main(String args[]) {
        B b = new B();
        b.method2();
    }
}

Ответы [ 6 ]

5 голосов
/ 24 апреля 2009

Я не верю в это; нет, если вы переопределяете method1() в подклассе. Если вам действительно требуется такое поведение, вам придется объявить A.method1() как final, и вы не сможете определить его в B.

Мне не имеет смысла делать это - вы должны пересмотреть свой дизайн, если считаете, что вам нужно!

1 голос
/ 24 апреля 2009

Для этого вам нужно использовать метод method1 не виртуальный. Чтобы сделать это, вы делаете его окончательным:

  public final void method1() {....

Или вы делаете это частным; в Java методы pivate всегда не виртуальны:

   private void method1() {....

(Обратите внимание, что в C ++ частные методы могут быть виртуальными или не виртуальными; среди прочего, это делает реализацию Template Method Pattern более чистой в C ++.)

Вот что происходит: когда нестатический метод вызывается через ссылку на объект (что является единственным способом вызова метода нестатического объекта в Java), вызываемый метод зависит от фактического типа упомянутого объекта. (указывает), а не тип ссылки.

В методе объекта вызовы других методов (или членов) этого объекта неявно имеют префикс «this». Итак, ваш вызов method1 в method2 действительно:

public void method2() {
    this.method1();
    System.out.println("A2");
}

И this является ссылкой на объект. В методе 2, который находится в классе A, ссылка имеет тип A, как если бы было объявлено this:

A this;

Но эта ссылка указывает на объект типа B; это может быть сделано, потому что B является производным от, наследует, подклассов, is-a, A.

Как я упоминал выше, «когда нестатический метод вызывается через ссылку на объект, вызываемый метод зависит от фактического типа объекта, на который делается ссылка, а не от типа ссылки». Когда вы создаете экземпляр объекта типа B и вызываете method2 (), переданный вами this (this на самом деле является скрытым параметром любой нестатической функции) - это this, который указывает на объект B. Когда B.method2 () вызвал super (), тот же this был передан A.method2 ().

Итак, когда A.method2 () вызвал method1 (), то, что действительно произошло, мы назвали this.method1() с тем же this, что относится к B, B, который вы создали в main() и вызвал method2 () on.

Поскольку method1 () является виртуальным, и поскольку мы вызываем method1 () для ссылки на объект типа B, компилятор гарантирует, что B переопределит Метод1 () - это тот, который называется.

1 голос
/ 24 апреля 2009

Да, вы можете сделать это. Определите A в пакете a :

package a;
public class A {
    void method1() {
        System.out.println("A1");
    }
    public void method2() {
        method1();
        System.out.println("A2");
    }
}

Определить B в упаковке b :

package b;
import a.A;
public class B extends A {
    @Override public void method2() {
        super.method2();
        System.out.println("B2");
    }
    void method1() {
        System.out.println("B1");
    }
}

Поместите свой тест в пакет a и запустите его. Результат - А1 / А2 / В2. Конечно, это вредно для здоровья: обратите внимание на необходимое упущение @Override для method1 - если вы вернете его обратно, вы получите ошибку компилятора:

method does not override or implement a method from a supertype
1 голос
/ 24 апреля 2009

Вы также можете сделать A.method1 приватным. Тогда, даже если у вас есть B.method1, вместо этого будет вызван A.method1. Но я не уверен в этом, вы должны проверить

0 голосов
/ 22 сентября 2011

Даже я искал это решение. Кажется, это невозможно. Изменение базового класса не вариант. то есть, предполагая, что базовый класс уже написан, и вы выводите класс из него, вы ничего не можете сделать в производном классе, чтобы базовый класс вызывал свои собственные методы, а не переопределенные методы. если вы создаете объект из производного класса, базовый класс будет вызывать переопределенные методы производного класса вместо вызова его собственных методов. это упс ....

0 голосов
/ 24 апреля 2009

Используя стандартные «естественные» и хорошо принятые механизмы Java, вы не можете. Java спроектирован так, что все методы отправляются динамически, кроме финальных или статических (или вызываемых к супер), и в вашем случае. Поскольку метод переопределен в B, ни один из них не является опцией. Не существует механизма для «отключения» динамической диспетчеризации в определенных случаях, но хорошая новость заключается в том, что в этом нет необходимости.

Вы можете преодолеть некоторые из этих ограничений с помощью рефлексии, но это все равно, что развернуть драгоценный подарок кувалдой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...