13

I have an init method that is used and overridden through out an extensive heirarchy. Each init call however extends on the work that the previous did. So naturally, I would:

@Override public void init() {
   super.init();
}

And naturally this would ensure that everything is called and instantiated. What I'm wondering is: Can I create a way to ensure that the super method was called? If all of the init's are not call, there is a break down in the obejct, so I want to throw an exception or an error if somebody forgets to call super.

TYFT ~Aedon

ahodder
  • 11,353
  • 14
  • 71
  • 114
  • Duplicate of http://stackoverflow.com/questions/4217013/how-to-force-derived-class-to-call-super-method-like-android-does and http://stackoverflow.com/questions/3982673/force-base-method-call – Ted Hopp May 26 '11 at 17:19
  • @Ted - the first link, you've posted does not look like a duplicate, the other question deals with eclipse's autogenerated method stubs, the other one is about c# and not java. – Andreas Dolk May 26 '11 at 17:22
  • @Andreas - the answers to the first one show several ways to force calling the superclass. The second one is tagged both c# and Java, and the answers are a mix of solutions. – Ted Hopp May 26 '11 at 17:29
  • You probably can create your own annotation for something like this. – Amir Afghani May 26 '11 at 17:29
  • @Amir - Pardon my ignorance, but how would I create an annotation to do something like this? I am very unfamiler with annotations in general. – ahodder May 26 '11 at 17:33
  • @Aedon, pardon *my* ignorance because I'm not an annotations expert. You'll have to read up on it - but the basic idea is that your annotation will statically analyze the code for this prior to execution. – Amir Afghani May 26 '11 at 17:36

7 Answers7

13

Rather than trying to do that -- I don't think it's achievable btw! -- how about a different approach:

abstract class Base {
 public final void baseFunction() {
   ...
   overridenFunction(); //call the function in your base class
   ...
 }

 public abstract void overridenFunction();
}
...
class Child extends Base {
 public void overridenFunction() {...};
}

...
Base object = new Child();
object.baseFunction(); //this now calls your base class function and the overridenFunction in the child class!

Would that work for you?

Liv
  • 6,006
  • 1
  • 22
  • 29
  • The same thing can be achieved by just overriding overridenFunction and super calling the polymorphed overridenFunction. There would be no point to creating the baseFunction method. – ahodder May 26 '11 at 17:31
  • Unless I completely missed something. Not unlikely. :) – ahodder May 26 '11 at 17:32
  • 4
    That doesn't work for 2-nd generation derived classes. If you define `class Grandchild extends Child`, then you can't guarantee that `Grandchild.overridenFunction` will call up to `Child.overridenFunction`. – Ted Hopp May 26 '11 at 17:33
  • Yes exactly.. abstract class are meant to do this – jmj May 26 '11 at 17:33
  • 1
    @Ted - Right, that's what I'm trying to solve. If `Class` calls `foo`, then `ChildClass` overrides `foo` and fails to call `super.foo`, there is a hierarchal breakdown in fooness when `GrandChildClass` tries to call `super.foo`. That is what I'm trying to stop. – ahodder May 26 '11 at 17:38
  • excellent; although different names for the methods here like `init()` and `childInit()` might help convey the intent better – matt b May 26 '11 at 17:41
  • 1
    @Ted, I think the best approach is to have different names, like a grandchildInit() here. The only situation in Java where the OP's desired behavior is enforceable is the ctor chain. If the object can not be inited at the point of construction, we get this setup. See http://en.wikipedia.org/wiki/Call_super. – Andrew Lazarus May 26 '11 at 17:51
9

Here's one way to raise an exception if a derived class fails to call up to the superclass:

public class Base {
    private boolean called;
    public Base() { // Doesn't have to be the c'tor; works elsewhere as well.
                    // In fact, shouldn't call overridable methods from c'tor.
        called = false;
        init();
        if (!called) {
            // throw an exception
        }
    }
    protected void init() {
        called = true;
        // other stuff
    }
}
Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • I like this. It will require a little tweaking, I think, to work into what I'm doing and it is still runtime, but I think this will work pretty good if what I hope to achieve isn't possible. – ahodder May 26 '11 at 17:41
  • 1
    I don't think there's a compile-time solution for the general problem. The solution others have proposed using a `final` init method works if the only concern is that the base class init logic is executed. This is the only approach I know of that checks that a derived class's method has called its `super` method. (I guess even that can be broken by a grandchild class that explicitly calls `Base.this.init()`, thereby skipping `Child.init()`. But that's just evil.) – Ted Hopp May 26 '11 at 17:45
  • That is true. I think this (and similar) is the better solution then. Thanks. – ahodder May 26 '11 at 17:49
  • there are few proposals for compile-time enforcement in this post: https://stackoverflow.com/questions/57300379/how-to-force-derived-class-to-call-super-class-method-at-multiple-layers – morgwai Aug 23 '23 at 13:06
2

Android actually accomplishes this in the Activity class. I'm not sure how or whether they had to build support into the runtime for it, but I'd check out the open source code for the Activity class implementation. Specifically, in any of the lifecycle methods, you have to call the corresponding super class method before you do anything otherwise it throws SuperNotCalledException.

For instance, in onCreate(), the first thing you have to do is call super.onCreate().

Chris Thompson
  • 35,167
  • 12
  • 80
  • 109
  • @ Andreas_D - Yessir. It would be nice to have a complilation error before we got to runtime, save some time testing. – ahodder May 26 '11 at 17:28
  • 2
    @Andreas - _"I want to throw an exception or an error"_ doesn't sound like OP expects a compile-time solution. – Ted Hopp May 26 '11 at 17:30
  • See [How did Android implement the checks for SuperNotCalledException?](http://stackoverflow.com/q/11867246/403455) – Jeff Axelrod Aug 08 '12 at 15:02
1

I frequently like to use this solution. It wont throw a runtime error, but it will show a syntax error:

 @CallSuper
 public void init() {
     // do stuff
 }

This is a part of Android support annotations.

Chad Bingham
  • 32,650
  • 19
  • 86
  • 115
0

Nowadays you can annotate your method with @CallSuper. This will Lint check that any overrides to that method calls super(). Here's an example:

@CallSuper
protected void onAfterAttached(Activity activity) {
    if (activity instanceof ActivityMain) {
        mainActivity = (ActivityMain) activity;
    }
}

In the example above, any methods in descendant classes that override onAfterAttached but do not call super will make Lint raise an error.

AlxDroidDev
  • 582
  • 7
  • 17
0

Make the class at the top of the inheritance tree set a flag on initialization. Then a class in the bottom of the inheritance tree can check for that flag to see if the whole tree has been traversed. I would make documentation that every child of base should include the following init code:

super.init()
if (!_baseIsInitialized) {
    // throw exception or do w/e you wish
}

where base uses

_baseIsInitialized = true;

The other way around, forcing your childs to call super.init() is a lot thougher and would most likely include ugly hacks.

orlp
  • 112,504
  • 36
  • 218
  • 315
  • 1
    but how to enforce child to check that – jmj May 26 '11 at 17:19
  • Interesting solution. Is this the only way? I was hoping (maybe naively) for something more elegant. – ahodder May 26 '11 at 17:20
  • @Jigar Joshi: That's a though one. – orlp May 26 '11 at 17:21
  • @Jigar: I don't think it's easily possible, and not at all at compile-time. It's impossible to the tell the compiler this: "after tokenizing `child.init` check if it contains `super.init()`, if not throw an error". – orlp May 26 '11 at 17:30
0

I don't know of any way to do this with a method.

However, note that this is exactly how constructors work. Every constructor must, directly or indirectly, call one of its superclass's constructors. This is statically guaranteed.

I note that you are writing an init method. Could you refactor so that your code uses constructors rather than init methods? That would give you this behaviour right out of the gate. Some people (eg me) prefer constructors to init methods anyway, partly for just this reason.

Note that using constructors rather than init methods might not mean using them on the class you're currently looking at - there might be a refactoring which moves the state needing initialisation out into a parallel class hierarchy which can use proper constructors.

Tom Anderson
  • 46,189
  • 17
  • 92
  • 133