Как заставить полиморфный вызов метода супер?
У меня есть метод init, который используется и переопределяется через обширную иерархию. Каждый вызов init, однако, распространяется на работу, выполненную предыдущим. Естественно, я бы:
@Override public void init() {
super.init();
}
И, естественно, это обеспечит, чтобы все вызывалось и создавалось. Мне интересно: могу ли я создать способ гарантировать, что был вызван метод super? Если все init не являются вызовом, в obejct есть разрыв, поэтому я хочу выбросить исключение или ошибку, если кто-то забывает называть super
.
TYFT ~ Aedon
Ответы
Ответ 1
Здесь один из способов повысить исключение, если производный класс не может вызвать суперкласс:
public class Base {
private boolean called;
public Base() { // doesn't have to be the c'tor; works elsewhere as well
called = false;
init();
if (!called) {
// throw an exception
}
}
protected void init() {
called = true;
// other stuff
}
}
Ответ 2
Вместо того, чтобы пытаться это сделать, я не думаю, что это достижимо кстати! - как насчет другого подхода:
abstract class Base {
public final void baseFunction() {
...
overridenFunction(); //call the function in your base class
...
}
public abstract void overridenFunction();
}
...
class Child extends Base {
public void overridenFunction() {...};
}
...
Base object = new Child();
object.baseFunction(); //this now calls your base class function and the overridenFunction in the child class!
Будет ли это работать для вас?
Ответ 3
Android действительно выполняет это в классе Activity
. Я не уверен, каким образом или нужно ли им создавать поддержку для среды выполнения, но я бы посмотрел открытый исходный код для реализации класса Activity
. В частности, в любом из методов жизненного цикла вам нужно вызвать соответствующий метод суперкласса, прежде чем делать что-либо иначе, иначе он выталкивает SuperNotCalledException
.
Например, в onCreate()
первое, что вам нужно сделать, это вызвать super.onCreate()
.
Ответ 4
Мне часто нравится использовать это решение. Это не вызовет ошибку времени выполнения, но будет отображаться синтаксическая ошибка:
@CallSuper
public void init() {
// do stuff
}
Это часть аннотаций Android поддержки.
Ответ 5
Сделать класс в начале дерева наследования установленным флажком при инициализации. Затем класс в нижней части дерева наследования может проверить этот флаг, чтобы увидеть, прошло ли все дерево. Я хотел бы сделать документацию о том, что каждый дочерний элемент base
должен включать следующий код инициализации:
super.init()
if (!_baseIsInitialized) {
// throw exception or do w/e you wish
}
где base использует
_baseIsInitialized = true;
Другим способом, заставляя ваших детей называть super.init()
много, и, скорее всего, будет включать уродливые хаки.
Ответ 6
Я не знаю, как это сделать с помощью метода.
Однако обратите внимание, что именно так работают конструкторы. Каждый конструктор должен прямо или косвенно вызвать один из своих конструкторов суперклассов. Это статически гарантировано.
Я отмечаю, что вы пишете метод init. Не могли бы вы реорганизовать, чтобы ваш код использовал конструкторы, а не методы init? Это даст вам такое поведение прямо из ворот. Некоторые люди (например, я) в любом случае предпочитают конструкторы для инициализации методов, отчасти по этой причине.
Обратите внимание, что использование конструкторов, а не методов init может не означать использование их в классе, который вы сейчас просматриваете, - может быть рефакторинг, который перемещает состояние, требующее инициализации, в иерархию параллельного класса, которая может использовать соответствующие конструкторы.
Ответ 7
В настоящее время вы можете аннотировать свой метод с помощью @CallSuper
. Это заставит Lint проверить, что любые переопределения этого метода вызывает super(). Вот пример:
@CallSuper
protected void onAfterAttached(Activity activity) {
if (activity instanceof ActivityMain) {
mainActivity = (ActivityMain) activity;
}
}
В приведенном выше примере любые методы в классах потомков, которые переопределяют onAfterAttached, но не называют super, заставят Lint вызывать ошибку.