简体   繁体   English

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

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

Let's say I have a class, which implements a method (addThings()).假设我有一个类,它实现了一个方法 (addThings())。 It serves as a foundation of a tree of subclasses:它作为子类树的基础:

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

Now, let's say we implement a child class (which has Thing 3 as well) and Thing 3 also needs to be added on top of Thing 1 and Thing 2 .现在,假设我们实现了一个子类(它也有 Thing 3)并且 Thing 3 也需要添加到 Thing 1 和 Thing 2 之上

The obvious Java solution seems to be to have the child's class's implementation of the method call the super's method:显而易见的 Java 解决方案似乎是让子类的方法实现调用 super 的方法:

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

The problem is that whoever implements the subclass may very well forget to do that, and have a bug :问题是实现子类的人很可能忘记这样做,并且有一个错误

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

Is there a way in Java to force any of the extending child (and grandchild) classes to always call a parent's method if they override it? Java 中有没有办法强制任何扩展的子(和孙)类在覆盖它时始终调用父类的方法? (similar to how making a method abstract always forces them to implement it). (类似于如何使方法抽象总是迫使他们实现它)。

  • Please note that I need the solution to be propagatable down the inheritance tree.请注意,我需要将解决方案沿继承树向下传播。

    In other words, if someone implements GrandChildClass which needs to add Thing4, they would suffer from the same bug possibility relative to ChildClass.换句话说,如果有人实现了需要添加 Thing4 的 GrandChildClass,他们将遭受与 ChildClass 相同的错误可能性。

    This means that the simple fix (applicable when you only have 1 level of inheritance) of having separate "addParentThings()" in ParentClass and then calling both addParentThings() and child-overridable empty addThings() is insufficient ( because the grandchild has to override non-empty addThings ).这意味着在 ParentClass 中有单独的“addParentThings()”然后调用 addParentThings() 和 child-overridable 空 addThings() 的简单修复(当你只有 1 级继承时适用)是不够的(因为孙子必须覆盖非空 addThings )。

Maybe try having a final method that calls another overridable method?也许尝试使用调用另一个可覆盖方法的最终方法?

class ParentClass {

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

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

And then in the child class:然后在子类中:

class ChildClass extends ParentClass {

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

You could even make the onPostDoStuff() method abstract, so children have to override it.您甚至可以使onPostDoStuff()方法抽象,因此孩子们必须覆盖它。

If you are willing to make your doStuff -Methods static for each class, which extends your ParentClass and give your ParentClass a final public void doAllStuff() -Method, you can solve the problem with Reflection:如果您愿意为每个类设置静态的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 {}

If you need to only call attributes, you can use getDeclaredField , the fields may not be static in this case.如果您只需要调用属性,您可以使用getDeclaredField ,在这种情况下,字段可能不是static

The following approach enforces invocation of the superclass' setup method.以下方法强制调用超类的 setup 方法。 The downside, or likely bug in subclasses, is that implementers might forget to provide a suitable constructor for providing an extension to the setup method.缺点或子类中可能存在的错误是实现者可能忘记提供合适的构造函数来为 setup 方法提供扩展。 This means that child couldn't be extended by a grandchild.这意味着孩子不能由孙子扩展。

Its also ugly because subclasses can't put subclass-specific setup in their extensions;它也很丑,因为子类不能在它们的扩展中放置子类特定的设置; they have to accept Parent as a parameter.他们必须接受Parent作为参数。

Overall, the awkwardness difficulties here suggest that enforcement is better done, if at all, in a static analyzer, rather than javac .总的来说,这里的笨拙困难表明在静态分析器中执行更好,如果有的话,而不是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");
  }

}

This is a slightly different approach for solving your problem, as in one of the comments to the selected answer says, you can use the decorator pattern (it is a little different from a traditional decorator, adapted to this problem), it is a cleaner solution in my opinion.这是解决您的问题的一种略有不同的方法,正如对所选答案的评论之一所说,您可以使用装饰器模式(它与传统的装饰器略有不同,适应了这个问题),它是一个更清洁的在我看来的解决方案。 I added 2 classes that add thing 3 and thing 4 to show the usage.我添加了 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);
  }
}

After running AdderApp this is printed:运行 AdderApp 后,打印:

Decorated Thing3Adder map:[thing 1, thing 2, thing 3]
Decorated Thing4Adder map:[thing 1, thing 2, thing 4]

The idea behind the decorator pattern is to augment existing functionality, in this case we are augmenting the addThings method by using a default decoration that adds thing 1 and thing 2 before calling the decorated object own addThings method, then whenever it is needed to have a new adder which requires the default values to be inserted first, the developer will just create a new ThingXAdder that extends the AbstractAdder.装饰器模式背后的想法是增强现有功能,在这种情况下,我们通过使用默认装饰来增强 addThings 方法,该装饰在调用被装饰对象自己的 addThings 方法之前添加事物 1 和事物 2,然后每当需要有一个需要先插入默认值的新加法器,开发人员只需创建一个扩展 AbstractAdder 的新 ThingXAdder。

I can't think of anything that would satisfy your condition of still being enforceable in future subclasses.我想不出有什么可以满足您在未来的子类中仍然可以执行的条件。

The Android sdk has the "super has not been called" exceptions in many of their core lifecycle methods, but it is strictly single level inheritance. Android sdk 的许多核心生命周期方法中都有“super has not been called”异常,但它是严格的单级继承。

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

相关问题 在Java中,如何从派生类中的重写方法调用基类的方法? - In Java, how do I call a base class's method from the overriding method in a derived class? 如何在不实例化任何子类的情况下调用在子类之间通用的抽象类方法 - How could I call an abstract class method who is common between subclasses without instanciating any subclass 在Java中,如何从重写类的实例调用重写类的方法 - In Java, how do I call the method of a overridden class from an instance of the overriding class 如何在JAVA中从父类的内部类中调用子类中的重写方法? - How can I call the overridden method in a child class from a parent class's inner class in JAVA? Java-如何用字符串调用类的方法? - Java - How do I call a class's method with a string? 如何在覆盖实现中调用接口的默认方法实现? - How to invoke an interface's default implementation of a method in an overriding implementation? 如何从Country类中调用对象方法? - How do I call an object method from my Country class? 在子类中强制执行toString()实现 - Force toString() implementation in subclasses 如何在要使用PowerMock测试的模拟抽象类中存根父类方法调用 - How to stub parent class method call in my mocked abstract class that I want to test using PowerMock 如何从子类调用重写的父类方法? - How do I call an overridden parent class method from a child class?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM