Search code examples
javainheritancecode-reusesuperdynamic-dispatch

In Java: Code reuse possible for a chain of method calls up an inheritance hierarchy?


I have some class inheritance SubClass < MidClass < SuperClass and want to perform some TASK upward for all these classes. TASK is quite complex with only minor changes in the 3 classes, which I moved into the private methods m2().

My current solution is very boiler plate:

class SuperClass {
  protected void m1() {
    //TASK (calls m2())
  }

  private void m2() {
    //...
  }
}

class MidClass extends SuperClass {
  protected void m1() {
    //same TASK (calls m2())
    super.m1();
  }

  private void m2() {
    //...
  }
}

class SubClass extends MidClass {
  protected void m1() {
    //same TASK (calls m2())
    super.m1();
  }

  private void m2() {
    //...
  }
}

Can I exploit some code reuse mechanism instead of copying TASK?

Something like the following, with m1() only in SuperClass, does not work:

class SuperClass {
  protected final void m1() {
    //TASK (calls m2())
    if (!(this.getClass().equals(SuperClass.class))) {
      super.m1();
  }
}

because super.m1() does not refer to execution of the same inherited method in the context of a super class, but to the overridden method implementation. Since m1() does not exist in Object, I additionally get a compiler error...

Putting TASK in a protected final helper() method in SuperClass and calling helper() instead of copying TASK won't work, since then always SuperClass.m2() gets called.

The only alternative I can think of is slow, complicated and unsafe: using a type token as parameter, i.e. protected final void m1(Class<? extends SuperClass> clazz) in SuperClass, and fulfilling TASK via reflection (requires to make m2() public static or use setAccessible(true) on m2()).

Do you know some better solution? AOP? Maybe some framework where you can inject a method into classes (as in C#)? Or am I missing something???


Solution

  • Solution for my concrete example of mixed-type equals() with default value constraints instead of ignoring the subclass value fields. Instead of Angelika Langer's solution (see http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals-2.html) with private methods _compareFields() and a protected method _navigateClassHierarchy() that has to be copied into each subclass, only a protected method compareOwnFields() is used, which has to be overridden correctly in each subclass.

    class SuperClass {
        // ...
    
        @Override
        public final boolean equals(final Object other) {
            if (other == this) { return true; }
            if (!(other instanceof SuperClass)) {
                return false;
            }
            final SuperClass otherSuperClass = (SuperClass) other;
    
            return compareOwnFields(otherSuperClass, false)  
            && otherSuperClass.compareOwnFields(this, true);
        }
    
        protected boolean compareOwnFields(final SuperClass other, 
            final boolean firstTraversal) {
            if (!firstTraversal) {
                return true;
            }
            if (field1 != other.getField1()) {
               return false;
            } 
            // compare other fields similarly ...
            return true;
        }
    
    }    
    
    class SubClass {
        // ...
    
        @Override
        protected boolean compareOwnFields(final SuperClass other, 
            final boolean firstTraversal) {
            if (other instanceof SubClass && !firstTraversal) {
                return true;
            if (other instanceof SubClass) {
                if (field1 != ((SubClass) other).getField1()) {
                    return false;
                }
                // compare other fields similarly ...
                return super.compareOwnFields(other, firstTraversal);
            } else {
                if (field1 != DEFAULT_FIELD1) {
                    return false;
                }
                // check other fields for default values similarly ..
                return super.compareOwnFields(other, firstTraversal);
            }
        }
    }
    

    But this does not answer my question in general, it's rather a redesign that avoids the problem. So further answers on how to solve the problem with the Java language features are very welcome!