How do I force a polymorphic call to the super method?

拈花ヽ惹草 提交于 2019-11-29 03:43:53

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
        called = false;
        init();
        if (!called) {
            // throw an exception
        }
    }
    protected void init() {
        called = true;
        // other stuff
    }
}

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?

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

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.

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.

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.

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!