4

Is there some type of @annotation, or other method, in Java to enforce method extension, instead of overriding?

To be specific, let's say I have a class Foo:

class Foo {
    public void bar(Thing thing) {
        // ...
    }
}

Is there a way I can enforce, at compile time, that any class X that extends Foo, and also overrides bar, makes a call to super.bar(thing) first?

torquestomp
  • 3,304
  • 19
  • 26

5 Answers5

3

No, you have to explicitly write it.

Side note for constructors: the superclass' nullary constructor will be implicitly called when instantiating the subclass, however many parameters the latter's constructor has.

sp00m
  • 47,968
  • 31
  • 142
  • 252
  • 2
    note, nullary constructor is implicitly called only when no other constructor is called explicetly (you can call another constructor both from superclass and this class). – Alexei Kaigorodov Apr 15 '13 at 18:21
  • @AlexeiKaigorodov ...and a nullary constructor must be available in the super class (either explicitly, or by defining no constructors at all), and it must not be `private`. – gaborsch Apr 15 '13 at 18:31
1

Generally, what you can do is to create a final method, that calls the extendable one.

class Foo {

    @Override
    public final void bar(Thing thing) {
        // super code comes here
        this.doBar(thing);
    }

    public abstract void doBar(Thing thing);

}

When you call

foo.bar(thing);

your super code runs first, then the code from the child class.

This way you can protect your full bar logic, and allow only certain parts to be extended/reimplemented.

Also, it allows you to postprocess the result, or to break up your code to certain subtasks.

gaborsch
  • 15,408
  • 6
  • 37
  • 48
  • I use this sort of pattern all the time, the problem is that it doesn't work for > 1 levels of inheritance. The `doBar` method would have the same contract/problem as the original `bar` method, or you would have to start a `doDoBar`, `doDoDoBar`, ... paradigm. – torquestomp Apr 15 '13 at 18:22
  • @torquestomp that's true, better try to find a proper name for the wrapped logic. Actually I never had to use it in multiple layers. – gaborsch Apr 15 '13 at 18:25
1

You could declare bar to be final, then call an abstract method from bar, which would force subclasses to implement an "extension".

abstract class Foo {
    public final void bar(Thing thing) {
        barImpl(thing);
        overrideMe(thing);
    }

    private final void barImpl(Thing thing) {
        // Original implementation of "bar" here.
    }

    protected abstract void overrideMe(Thing thing);
}

EDIT

I've changedoverrideMe from public to protected so users of Foo can't just call overrideMe instead of bar.

rgettman
  • 176,041
  • 30
  • 275
  • 357
  • Like I noted on GaborSch's answer, this method only delays the problem somewhat. If there is a second level of inheritance, the `overrideMe` method acquires the exact same problem as the original `bar` method. – torquestomp Apr 15 '13 at 18:29
0

While you cannot force code to call up to its superclass at compile time, it's not too hard to detect at run time when code does not call up to the superclass.

class Foo {
    private boolean baseCalled;
    public final void bar(Thing thing) {
        baseCalled = false;
        barImp(thing);
        if (!baseCalled) {
            throw new RuntimeException("super.barImp() not called");
        }
    }
    protected void barImp(Thing thing) {
         baseCalled = true;
         . . . // base class implementation of bar
    }
}

Note that this extends to multiple levels of inheritance without further elaboration. The method works particularly well for methods that are called from within Foo; in those cases, you can often forgo the final qualifier and redirection to an implementation method, and just define the base class method to set the flag. Clearing the flag would be done at each point of invocation.

The above pattern is used extensively in the Android framework. It doesn't guarantee that super.barImp was called as the first thing in subclass overrides; just that it was called.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
0

You can try to use @AroundInvoke annotation, if you are using EJBs.

By using reflection, you can find the same method in your parent class, and yo can invoke it with the same parameters as the original method was called.

Note, that in this case you must avoid super.bar(thing) calls, otherwise they would be called twice.

gaborsch
  • 15,408
  • 6
  • 37
  • 48