Java, вызов переопределенного метода неявно

Прямо сейчас в каком-то Java-коде у меня есть что-то вроде этого

class A {
 void f() {

 }
 A() {
  f();
 }
}

class B extends A{
 @Override
 void f() {
  //do some stuff
  super.f();
 }
}

class C extends B {
 @Override
 void f() {
  //do some stuff
  super.f();
 }
}

Я хочу вызвать f(), а затем выполнить итерацию вверх по каждому родительскому классу, запустив переопределенный f(). Я делаю это, явно вызывая super.f(), хотя мне бы не хотелось этого делать.

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

Итак, конструктор A действительно

A() {
  //init some state in a
  f(); //run f(), which will depend on the previous operation
}

Если я сделаю что-то вроде new C();, я хочу, чтобы вызывался C.f(), затем вызывался B.f(), затем вызывался A.f()

В любом случае, если есть более разумный способ сделать это, я открыт для него.


person Mike    schedule 02.10.2009    source источник


Ответы (3)


Вызов методов, не являющихся конечными, в конструкторе, как правило, плохая идея — по крайней мере, я бы посоветовал вам тщательно задокументировать это. Имейте в виду, что при вызове f() конструктор C не будет вызываться, равно как и инициализаторы переменных. Объект только наполовину инициализирован, поэтому методы должны быть написаны очень тщательно.

В обычной Java нет возможности неявно вызвать super.f(). Учитывая большие предупреждения, которые я буду размещать вокруг этого кода, одно утверждение далеко от конца света :)

Если вы хотите убедиться, что он вызван, вы всегда можете проверить результаты A.f() в конструкторе A сразу после вызова - это проверит, что вызов достиг верхнего уровня.

person Jon Skeet    schedule 02.10.2009
comment
+1 за вызов нефинальных методов в конструкторе, как правило, плохая идея (из опыта :-) - person Jim Garrison; 03.10.2009
comment
Мне не нужно запускать какие-либо конструкторы, пока выполняется первая часть A(), мы в хорошей форме. Это часть хака для сериализации без использования интерфейса сериализации с использованием частного пространства имен. В основном все классы заранее объявляют переменные, которые они собираются использовать, но A init — это частное пространство имен. Спасибо за ответ, Джон, я подумал, что это невозможно, приятно иметь второе мнение - person Mike; 03.10.2009
comment
Тем не менее, иногда привлекательно использовать шаблон метода шаблона в конструкторах. Пока абстрактные (или не окончательные) методы реализуют алгоритм, не полагаясь на состояние, он работает. Другим решением является использование ленивой инициализации. У меня есть блог именно по этому вопросу: novyden.blogspot .com/2011/08/ - person topchef; 14.08.2011

Это может вам немного помочь - это способ заставить дочерний класс вызывать super; это поможет вам обнаружить ошибки разработчика, когда разработчик дочернего класса забыл продолжить вызов цепочки до super.f():

class A {
 boolean fDone;

 public final void f() {
  fDone = false;
  doF();
  if (!fDone) {
   throw new IllegalStateException("super.f() was not called");
  }
 }

 protected void doF() {
  //do some operation
  fDone = true;
 }
}

class B extends A {
 protected void doF() {
  //some operation, maybe repeat this pattern of using a flag to enforce super call
  super.doF();
 }
}

Таким образом, дочерний элемент переопределяет doF(), но требуется вызвать super.doF();

person RMorrisey    schedule 02.10.2009

Насколько я знаю, нет никакого способа сделать это. AspectJ может сделать что-то подобное, но тогда вам придется пометить его аннотацией, я полагаю, что вряд ли меньше работы, чем просто вызов super.f(). Вам нужно указать, что вызывается метод суперкласса, потому что вам нужно иметь возможность принимать это решение для каждого подкласса отдельно - в противном случае у вас может быть подкласс, который вообще не хочет ничего делегировать суперклассу - поэтому по умолчанию вы просто вводите код.

person jprete    schedule 02.10.2009