DVK
DVK

Reputation: 129491

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

Upvotes: 7

Views: 2494

Answers (5)

raspacorp
raspacorp

Reputation: 5317

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.

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:

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.

Upvotes: 1

Turing85
Turing85

Reputation: 20195

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.

Upvotes: 2

erickson
erickson

Reputation: 269807

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");
  }

}

Upvotes: 1

Andrew Arace
Andrew Arace

Reputation: 457

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.

Upvotes: 0

Anubian Noob
Anubian Noob

Reputation: 13596

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.

Upvotes: 6

Related Questions