繁体   English   中英

如何强制我的类的任何子类始终调用它们覆盖的父类的实现方法?

[英]How do I force any subclasses of my class to always call a parent's implementation method they are overriding?

假设我有一个类,它实现了一个方法 (addThings())。 它作为子类树的基础:

ParentClass {
    protected void addThings() {
        map.add(thing1);
        map.add(thing2);
    }
}

现在,假设我们实现了一个子类(它也有 Thing 3)并且 Thing 3 也需要添加到 Thing 1 和 Thing 2 之上

显而易见的 Java 解决方案似乎是让子类的方法实现调用 super 的方法:

ChildClass extends ParentClass {
    protected void addThings() {
        super.addThings();
        map.add(thing3);
    }
}

问题是实现子类的人很可能忘记这样做,并且有一个错误

ChildClassBad extends ParentClass {
    protected void addThings() {
        // BUG!!! Forgot to call super.addThings(); !!!
        map.add(thing3); 
    }
}

Java 中有没有办法强制任何扩展的子(和孙)类在覆盖它时始终调用父类的方法? (类似于如何使方法抽象总是迫使他们实现它)。

  • 请注意,我需要将解决方案沿继承树向下传播。

    换句话说,如果有人实现了需要添加 Thing4 的 GrandChildClass,他们将遭受与 ChildClass 相同的错误可能性。

    这意味着在 ParentClass 中有单独的“addParentThings()”然后调用 addParentThings() 和 child-overridable 空 addThings() 的简单修复(当你只有 1 级继承时适用)是不够的(因为孙子必须覆盖非空 addThings )。

也许尝试使用调用另一个可覆盖方法的最终方法?

class ParentClass {

    public final void doStuff() {
        // Do stuff
        onPostDoStuff();
    }

    protected void onPostDoStuff() {
        // Override this!
    }
}

然后在子类中:

class ChildClass extends ParentClass {

    @Override
    protected void onPostDoStuff() {
        // Do extra stuff
    }
}

您甚至可以使onPostDoStuff()方法抽象,因此孩子们必须覆盖它。

如果您愿意为每个类设置静态的doStuff -Methods,这会扩展您的ParentClass并为您的ParentClass提供final public void doAllStuff() ,您可以使用反射解决问题:

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

以下方法强制调用超类的 setup 方法。 缺点或子类中可能存在的错误是实现者可能忘记提供合适的构造函数来为 setup 方法提供扩展。 这意味着孩子不能由孙子扩展。

它也很丑,因为子类不能在它们的扩展中放置子类特定的设置; 他们必须接受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。

我想不出有什么可以满足您在未来的子类中仍然可以执行的条件。

Android sdk 的许多核心生命周期方法中都有“super has not been called”异常,但它是严格的单级继承。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM