Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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()). 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.

The obvious Java solution seems to be to have the child's class's implementation of the method call the super's method:

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? (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.

    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).

like image 429
DVK Avatar asked May 05 '15 19:05

DVK


People also ask

Can subclasses access parent methods?

A subclass does not inherit the private members of its parent class. However, if the superclass has public or protected methods for accessing its private fields, these can also be used by the subclass. A nested class has access to all the private members of its enclosing class—both fields and methods.

How you would call a parent class of an overriding method in a child class?

Super keyword in Method Overriding. The super keyword is used for calling the parent class method/constructor.

When an overridden method is called from within a subclass it will always refer to the version of that method defined by the?

When an overridden method is called from within a subclass, it will always refer to the version of that method defined by the subclass. The version of the method defined by the superclass will be hidden.

How do you invoke an overridden superclass method from a subclass?

It is what overriding for. The overridden method shall not be accessible from outside of the classes at all. But you can call it within the child class itself. to call a super class method from within a sub class you can use the super keyword.


3 Answers

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.

like image 160
Anubian Noob Avatar answered Nov 07 '22 14:11

Anubian Noob


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:

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.

like image 2
Turing85 Avatar answered Nov 07 '22 14:11

Turing85


The following approach enforces invocation of the superclass' setup method. 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. 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.

Overall, the awkwardness difficulties here suggest that enforcement is better done, if at all, in a static analyzer, rather than 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");
  }

}
like image 1
erickson Avatar answered Nov 07 '22 13:11

erickson