Могу ли я сказать, был ли вызван абстрактный метод?
Учитывая этот класс:
abstract class Foo{
public Foo(){...}
public abstract void checkable();
public void calledWhenCheckableIsCalled(){
System.out.println("checkable was called");
}
}
Есть ли код, который я могу поместить в конструктор Foo, чтобы сделать calledWhenCheckableIsCalled
позвонить, когда checkable
называется?
Примечание. Это грубое упрощение реального проекта, над которым я работаю.
Изменить: я уже реализовал шаблон обхода. Мне просто интересно, есть ли другой способ сделать это, которого мне не хватает. (Возможно, используя отражение.)
6 ответов
Выглядит как шаблон шаблона.
Но тогда вы должны реализовать Foo.checkable()
и ввести другой абстрактный метод для делегирования.
abstract class Foo{
public Foo(){}
public void checkable(){
calledWhenCheckableIsCalled();
doCheckable();
}
protected abstract void doCheckable();
public void calledWhenCheckableIsCalled(){
System.out.println("checkable was called");
}
}
Я бы также предложил сделать checkable()
окончательный в этом случае, так что вы можете быть уверены, что checkable()
не может быть реализовано по-другому, как вы ожидали.
В дополнение к комментарию Брайана Роуча
Недостатком является то, что защищенный может быть расширен до открытого в подклассе, так что вы не можете явно применить его.
Это правда, но вы можете предотвратить Foo
экземпляр от экземпляра, если подкласс увеличивает видимость doCheckable
, Поэтому вы должны вводить проверку всякий раз, когда создается объект. Я бы порекомендовал использовать код инициализатора, чтобы проверка выполнялась для каждого существующего конструктора. Тогда нельзя забывать призывать и, следовательно, быть обойденным.
Например:
abstract class Foo {
{ // instance initializer code ensures that enforceDoCheckableVisibility
// is invoked for every constructor
enforceDoCheckableVisibility();
}
public Foo() {...}
public Foo(Object o) {...}
private void enforceDoCheckableVisibility() {
Class<?> currentClass = getClass();
while (currentClass != Foo.class) {
try {
Method doCheckableMethod = currentClass.getDeclaredMethod("doCheckable");
if (Modifier.isPublic(doCheckableMethod.getModifiers())) {
throw new RuntimeException("Visibility of "
+ currentClass.getSimpleName()
+ ".doCheckable() must not be public");
}
} catch (SecurityException | NoSuchMethodException e) {}
currentClass = currentClass.getSuperclass();
}
}
}
Поскольку проверка реализована с использованием отражения, недостатком является то, что она проверяется только во время выполнения. Так что, конечно, у вас не будет поддержки компилятора. Но этот подход позволяет вам обеспечить, чтобы экземпляр Foo
может существовать, только если он выполняет ваш контракт.
Нет, конструктор будет вызван один раз во время инициализации объекта. Однако вы можете получить свой подкласс, который обеспечивает реализацию для вызова метода в суперклассе:
class Bar extends Foo {
// implementation of abstract method
public void checkable(){
super.calledWhenCheckableIsCalled(); // call to parent's method
...
}
}
РЕДАКТИРОВАТЬ
Вы могли бы достичь этого с помощью аспектов. Используя аспект, вы можете перехватывать каждый вызов метода, ссылаясь на абстрактный родительский метод. Это освобождает вас от вмешательства в детский код. Ваш calledWhenCheckableIsCalled
код затем станет частью перехватывающего кода.
abstract class Foo {
// use pointcut to intercept here
public void checkable();
}
Мне кажется, что вы ищете шаблон шаблона:
public abstract class Template {
public final void checkable() {
calledWhenCheckableIsCalled();
doCheckable();
}
protected abstract void doCheckable();
private void calledWhenCheckableIsCalled() {
System.out.println("checkable was called");
}
}
Теперь каждый раз checkable()
называется, calledWhenCheckableIsCalled()
также называется. И наследник должен по-прежнему обеспечивать фактическую реализацию checkable()
, осуществляя doCheckable()
метод.
Обратите внимание, что делает checkable()
final не позволяет подклассу переопределить его и, таким образом, обойти вызов calledWhenCheckableIsCalled()
,
abstract class foo {
public abstract void bar();
public void moo() {
System.out.println("some code");
this.bar();
System.out.println("more code");
}
}
сейчас если moo
называется, основная реализация bar
будет использоваться, это просто небольшой сдвиг парадигмы от того, что вы хотите.
так ваш конечный пользователь будет звонить moo
вместо bar
, но он все еще должен реализовать bar
Нет, у абстрактного метода нет тела. Вы можете, однако, связать ваш метод следующим образом:
abstract class Foo {
void callMeInstead() {
// do common
callMeImplementation();
}
abstract void callMeImplementation();
}
Там нет никакого способа, как вы заставляете этот метод реализовать в child
,
Неловкое предложение будет известно из реализации ребенка. Я имею в виду нет чистого пути AFAIK