我如何强制我的 class 的任何子 class 始终调用它们正在覆盖的父级实现方法?
How do I force any subclasses of my class to always call a parent's implementation method they are overriding?
假设我有一个 class,它实现了一个方法 (addThings())。它作为子classes:
树的基础
ParentClass {
protected void addThings() {
map.add(thing1);
map.add(thing2);
}
}
现在,假设我们实现了一个子项 class(它也有事物 3)并且事物 3 也需要添加 在事物 1 和事物 2 之上.
显而易见的 Java 解决方案似乎是让子 class 的方法实现调用父类的方法:
ChildClass extends ParentClass {
protected void addThings() {
super.addThings();
map.add(thing3);
}
}
问题是,谁实现了 subclass 很可能会忘记这样做,并且有一个错误:
ChildClassBad extends ParentClass {
protected void addThings() {
// BUG!!! Forgot to call super.addThings(); !!!
map.add(thing3);
}
}
Java有没有办法强制任何扩展子(和孙)class总是调用父方法,如果他们覆盖它? (类似于使方法抽象总是强制他们实现它的方式)。
请注意,我需要解决方案可以沿继承树传播。
换句话说,如果有人实现需要添加 Thing4 的 GrandChildClass,他们将遭受与 ChildClass 相同的错误可能性。
这意味着在 ParentClass 中具有单独的 "addParentThings()" 然后调用 addParentThings() 和子可重写的空 addThings() 的简单修复(适用于只有 1 级继承的情况)是不够的( 因为孙子要覆盖非空addThings).
也许尝试使用调用另一个可重写方法的最终方法?
class ParentClass {
public final void doStuff() {
// Do stuff
onPostDoStuff();
}
protected void onPostDoStuff() {
// Override this!
}
}
然后在child class:
class ChildClass extends ParentClass {
@Override
protected void onPostDoStuff() {
// Do extra stuff
}
}
您甚至可以将 onPostDoStuff()
方法抽象化,因此 children 可以覆盖它。
我想不出有什么可以满足你在未来的子类中仍然可执行的条件。
Android sdk 的许多核心生命周期方法都有"super has not been called"异常,但它是严格的单级继承。
如果您愿意为每个 class 设置静态的 doStuff
-Methods,这会扩展您的 ParentClass
并给您的 ParentClass
一个 final public void doAllStuff()
-方法,可以用Reflection解决问题:
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class Main
{
public static void main(String[] args) throws InterruptedException
{
A a = new C();
a.doAllStuff();
}
}
class A
{
protected List<String> list = new ArrayList<String>();
@SuppressWarnings("unused")
private static void doStuff(A a)
{
a.list.add("I am A");
}
final public void doAllStuff()
{
List<Class<?>> list = new ArrayList<Class<?>>();
Class<?> clazz = this.getClass();
while (A.class.getSuperclass() != clazz)
{
list.add(clazz);
clazz = clazz.getSuperclass();
}
System.out.println(list);
for (Class<?> myClass : list)
{
try
{
Method method = myClass.getDeclaredMethod("doStuff"
, myClass);
// Method is private? Make it accessible anyway.
method.setAccessible(true);
method.invoke(this, this);
}
catch (NoSuchMethodException e)
{
// Method not found, continue with next class.
continue;
}
catch (Exception e)
{
e.printStackTrace();
}
}
System.out.println(this.list);
}
}
class B extends A
{
@SuppressWarnings("unused")
private static void doStuff(B b)
{
b.list.add("I am B");
}
}
class C extends B {}
如果只需要调用属性,可以使用getDeclaredField
,这种情况字段可以不是static
以下方法强制调用超类的设置方法。缺点或子类中可能存在的错误是,实现者可能会忘记提供合适的构造函数来为设置方法提供扩展。这意味着 child 无法扩展 grandchild.
它也很难看,因为子类不能在它们的扩展中设置 subclass-specific;他们必须接受 Parent
作为参数。
总的来说,这里的尴尬困难表明,如果有的话,最好在静态分析器中执行,而不是 javac
。
public class Parent
{
private final Consumer<Parent> setup;
protected final Collection<Object> x = new ArrayList<>();
public Parent()
{
setup = Parent::setupImpl;
}
protected Parent(Consumer<Parent> extension)
{
setup = ((Consumer<Parent>) Parent::setupImpl).andThen(extension);
}
public final void setup()
{
setup.accept(this);
}
private static void setupImpl(Parent it)
{
it.x.add("thing1");
it.x.add("thing2");
}
}
public class Child
extends Parent
{
public Child()
{
super(Child::setupImpl);
}
protected Child(Consumer<Parent> extension)
{
super(((Consumer<Parent>) Child::setupImpl).andThen(extension));
}
private static void setupImpl(Parent it)
{
it.x.add("thing3");
}
}
这是解决您的问题的一种略有不同的方法,正如所选答案的评论之一所说,您可以使用装饰器模式(它与传统的装饰器略有不同,适用于此问题) ,在我看来这是一个更清洁的解决方案。我添加了 2 类 来添加事物 3 和事物 4 以显示用法。
public interface ThingAdder {
void addThings();
}
public abstract class AbstractAdder implements ThingAdder {
protected List<String> map = new ArrayList<>(); // or your map impl
}
public class DefaultAdderDecorator implements ThingAdder {
AbstractAdder decoratedThingAdder;
public DefaultAdderDecorator(AbstractAdder decoratedThingAdder) {
this.decoratedThingAdder = decoratedThingAdder;
}
@Override
public void addThings() {
decoratedThingAdder.map.add("thing 1");
decoratedThingAdder.map.add("thing 2");
decoratedThingAdder.addThings();
}
}
public class Thing3Adder extends AbstractAdder {
@Override
public void addThings() {
map.add("thing 3");
}
}
public class Thing4Adder extends AbstractAdder {
@Override
public void addThings() {
map.add("thing 4");
}
}
public class AdderApp {
public static void main(String args[]) {
Thing3Adder thing3Adder = new Thing3Adder();
Thing4Adder thing4Adder = new Thing4Adder();
ThingAdder decoratedAdder = new DefaultAdderDecorator(thing3Adder);
decoratedAdder.addThings();
System.out.println("Decorated Thing3Adder map:"+thing3Adder.map);
decoratedAdder = new DefaultAdderDecorator(thing4Adder);
decoratedAdder.addThings();
System.out.println("Decorated Thing4Adder map:"+thing4Adder.map);
}
}
在 运行 AdderApp 之后打印:
Decorated Thing3Adder map:[thing 1, thing 2, thing 3]
Decorated Thing4Adder map:[thing 1, thing 2, thing 4]
装饰器模式背后的想法是增强现有功能,在这种情况下,我们通过使用默认装饰来增强 addThings 方法,该装饰在调用装饰对象自己的 addThings 方法之前添加事物 1 和事物 2,然后每当它需要有一个需要首先插入默认值的新加法器,开发人员将只创建一个扩展 AbstractAdder 的新 ThingXAdder。
假设我有一个 class,它实现了一个方法 (addThings())。它作为子classes:
树的基础ParentClass {
protected void addThings() {
map.add(thing1);
map.add(thing2);
}
}
现在,假设我们实现了一个子项 class(它也有事物 3)并且事物 3 也需要添加 在事物 1 和事物 2 之上.
显而易见的 Java 解决方案似乎是让子 class 的方法实现调用父类的方法:
ChildClass extends ParentClass {
protected void addThings() {
super.addThings();
map.add(thing3);
}
}
问题是,谁实现了 subclass 很可能会忘记这样做,并且有一个错误:
ChildClassBad extends ParentClass {
protected void addThings() {
// BUG!!! Forgot to call super.addThings(); !!!
map.add(thing3);
}
}
Java有没有办法强制任何扩展子(和孙)class总是调用父方法,如果他们覆盖它? (类似于使方法抽象总是强制他们实现它的方式)。
请注意,我需要解决方案可以沿继承树传播。
换句话说,如果有人实现需要添加 Thing4 的 GrandChildClass,他们将遭受与 ChildClass 相同的错误可能性。
这意味着在 ParentClass 中具有单独的 "addParentThings()" 然后调用 addParentThings() 和子可重写的空 addThings() 的简单修复(适用于只有 1 级继承的情况)是不够的( 因为孙子要覆盖非空addThings).
也许尝试使用调用另一个可重写方法的最终方法?
class ParentClass {
public final void doStuff() {
// Do stuff
onPostDoStuff();
}
protected void onPostDoStuff() {
// Override this!
}
}
然后在child class:
class ChildClass extends ParentClass {
@Override
protected void onPostDoStuff() {
// Do extra stuff
}
}
您甚至可以将 onPostDoStuff()
方法抽象化,因此 children 可以覆盖它。
我想不出有什么可以满足你在未来的子类中仍然可执行的条件。
Android sdk 的许多核心生命周期方法都有"super has not been called"异常,但它是严格的单级继承。
如果您愿意为每个 class 设置静态的 doStuff
-Methods,这会扩展您的 ParentClass
并给您的 ParentClass
一个 final public void doAllStuff()
-方法,可以用Reflection解决问题:
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class Main
{
public static void main(String[] args) throws InterruptedException
{
A a = new C();
a.doAllStuff();
}
}
class A
{
protected List<String> list = new ArrayList<String>();
@SuppressWarnings("unused")
private static void doStuff(A a)
{
a.list.add("I am A");
}
final public void doAllStuff()
{
List<Class<?>> list = new ArrayList<Class<?>>();
Class<?> clazz = this.getClass();
while (A.class.getSuperclass() != clazz)
{
list.add(clazz);
clazz = clazz.getSuperclass();
}
System.out.println(list);
for (Class<?> myClass : list)
{
try
{
Method method = myClass.getDeclaredMethod("doStuff"
, myClass);
// Method is private? Make it accessible anyway.
method.setAccessible(true);
method.invoke(this, this);
}
catch (NoSuchMethodException e)
{
// Method not found, continue with next class.
continue;
}
catch (Exception e)
{
e.printStackTrace();
}
}
System.out.println(this.list);
}
}
class B extends A
{
@SuppressWarnings("unused")
private static void doStuff(B b)
{
b.list.add("I am B");
}
}
class C extends B {}
如果只需要调用属性,可以使用getDeclaredField
,这种情况字段可以不是static
以下方法强制调用超类的设置方法。缺点或子类中可能存在的错误是,实现者可能会忘记提供合适的构造函数来为设置方法提供扩展。这意味着 child 无法扩展 grandchild.
它也很难看,因为子类不能在它们的扩展中设置 subclass-specific;他们必须接受 Parent
作为参数。
总的来说,这里的尴尬困难表明,如果有的话,最好在静态分析器中执行,而不是 javac
。
public class Parent
{
private final Consumer<Parent> setup;
protected final Collection<Object> x = new ArrayList<>();
public Parent()
{
setup = Parent::setupImpl;
}
protected Parent(Consumer<Parent> extension)
{
setup = ((Consumer<Parent>) Parent::setupImpl).andThen(extension);
}
public final void setup()
{
setup.accept(this);
}
private static void setupImpl(Parent it)
{
it.x.add("thing1");
it.x.add("thing2");
}
}
public class Child
extends Parent
{
public Child()
{
super(Child::setupImpl);
}
protected Child(Consumer<Parent> extension)
{
super(((Consumer<Parent>) Child::setupImpl).andThen(extension));
}
private static void setupImpl(Parent it)
{
it.x.add("thing3");
}
}
这是解决您的问题的一种略有不同的方法,正如所选答案的评论之一所说,您可以使用装饰器模式(它与传统的装饰器略有不同,适用于此问题) ,在我看来这是一个更清洁的解决方案。我添加了 2 类 来添加事物 3 和事物 4 以显示用法。
public interface ThingAdder {
void addThings();
}
public abstract class AbstractAdder implements ThingAdder {
protected List<String> map = new ArrayList<>(); // or your map impl
}
public class DefaultAdderDecorator implements ThingAdder {
AbstractAdder decoratedThingAdder;
public DefaultAdderDecorator(AbstractAdder decoratedThingAdder) {
this.decoratedThingAdder = decoratedThingAdder;
}
@Override
public void addThings() {
decoratedThingAdder.map.add("thing 1");
decoratedThingAdder.map.add("thing 2");
decoratedThingAdder.addThings();
}
}
public class Thing3Adder extends AbstractAdder {
@Override
public void addThings() {
map.add("thing 3");
}
}
public class Thing4Adder extends AbstractAdder {
@Override
public void addThings() {
map.add("thing 4");
}
}
public class AdderApp {
public static void main(String args[]) {
Thing3Adder thing3Adder = new Thing3Adder();
Thing4Adder thing4Adder = new Thing4Adder();
ThingAdder decoratedAdder = new DefaultAdderDecorator(thing3Adder);
decoratedAdder.addThings();
System.out.println("Decorated Thing3Adder map:"+thing3Adder.map);
decoratedAdder = new DefaultAdderDecorator(thing4Adder);
decoratedAdder.addThings();
System.out.println("Decorated Thing4Adder map:"+thing4Adder.map);
}
}
在 运行 AdderApp 之后打印:
Decorated Thing3Adder map:[thing 1, thing 2, thing 3]
Decorated Thing4Adder map:[thing 1, thing 2, thing 4]
装饰器模式背后的想法是增强现有功能,在这种情况下,我们通过使用默认装饰来增强 addThings 方法,该装饰在调用装饰对象自己的 addThings 方法之前添加事物 1 和事物 2,然后每当它需要有一个需要首先插入默认值的新加法器,开发人员将只创建一个扩展 AbstractAdder 的新 ThingXAdder。