How to force derived class to call super method? (Like Android does)
Asked Answered
M

9

92

I was wondering, when creating new Activity classes and then overriding the onCreate() method, in eclipse I always get auto added: super.onCreate(). How does this happen? Is there a java keyword in the abstract or parent class that forces this?

I don't know if it is illegal not to call the super class, but I remember in some methods that I got a exception thrown for not doing this. Is this also built-in into java? Can you use some keyword to do that? Or how is it done?

Monkeypot answered 18/11, 2010 at 16:18 Comment(3)
I want to know this as well. And it's not just Eclipse being helpful, if I remove the call to super.onCreate(), the app gives a runtime error saying: "did not call super.onCreate()". So yes, it's really forcing you to call the super method. But how?Iconoscope
@RodrigoCastro You can review the javadocs for each method. For example onCreate().Main
related: #57300879Karelian
C
10

Here's the source of Activity#onCreate() - it is almost all comments (original - see line ~800):

/**
 * Called when the activity is starting.  This is where most initialization
 * should go: calling {@link #setContentView(int)} to inflate the
 * activity's UI, using {@link #findViewById} to programmatically interact
 * with widgets in the UI, calling
 * {@link #managedQuery(android.net.Uri , String[], String, String[], String)} to retrieve
 * cursors for data being displayed, etc.
 *
 * <p>You can call {@link #finish} from within this function, in
 * which case onDestroy() will be immediately called without any of the rest
 * of the activity lifecycle ({@link #onStart}, {@link #onResume},
 * {@link #onPause}, etc) executing.
 *
 * <p><em>Derived classes must call through to the super class's
 * implementation of this method.  If they do not, an exception will be
 * thrown.</em></p>
 *
 * @param savedInstanceState If the activity is being re-initialized after
 *     previously being shut down then this Bundle contains the data it most
 *     recently supplied in {@link #onSaveInstanceState}.  <b><i>Note: Otherwise it is null.</i></b>
 *
 * @see #onStart
 * @see #onSaveInstanceState
 * @see #onRestoreInstanceState
 * @see #onPostCreate
 */
protected void onCreate(Bundle savedInstanceState) {
    mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
            com.android.internal.R.styleable.Window_windowNoDisplay, false);
    mCalled = true;
}

so, my guess would be that the ADT Eclipse plugin is what's auto-adding that call to super.onCreate() for you. It's a total guess, though.

Caltrop answered 18/11, 2010 at 16:37 Comment(1)
I guess the mCalled = true also is used for a possible exception. Maybe not in the onCreate() but when indeed such a exception is thrown it will use that simple pattern.Monkeypot
P
207

This is added in the support annotation library:

dependencies {
    compile 'com.android.support:support-annotations:22.2.0'
}

http://tools.android.com/tech-docs/support-annotations

@CallSuper

Pulling answered 9/6, 2015 at 17:44 Comment(4)
Sorry but I need to change my comment. It didn't forced to implement super method. I was able to run app without supper call.Tambourine
@RizwanSohaib it won't force you to do anything. It will highlight and let you know you need to call it. To do anything more complex, you will need either an annotation processor or to implement the logic yourself. depending on the IDE, it should prevent you from building as well.Kimball
You can enforce @CallSuper on release builds by using lint check "MissingSuperCall" tools.android.com/tips/lint-checksCarom
There is an issue with this approach if you have multi module project, and you need to use this in non Android module, it will not work.Show
P
86

If you want to force subclasses to execute the parent class' logic, a common pattern is something like the following:

public abstract class SuperClass implements SomeInterface
{
    // This is the implementation of the interface method
    // Note it's final so it can't be overridden
    public final Object onCreate()
    {
        // Hence any logic right here always gets run
        // INSERT LOGIC

        return doOnCreate();

        // If you wanted you could instead create a reference to the
        // object returned from the subclass, and then do some
        // post-processing logic here
    }

    protected abstract Object doOnCreate();
}

public class Concrete extends SuperClass
{
    @Override
    protected Object doOnCreate()
    {
        // Here's where the concrete class gets to actually do
        // its onCreate() logic, but it can't stop the parent
        // class' bit from running first

        return "Hi";
    }
}

This doesn't actually answer your question about what prompts Eclipse to automatically insert a superclass call into the implementation; but then I don't think that's the way to go anyway as this can always be deleted.

You can't actually enforce that a method must call the superclass' version with a Java keyword, or anything like that. I suspect that your exceptions simply came from some code in the parent class checking expected invariants, or something, that were invalidated by your approach. Note that this is subtly different from throwing an exception because you failed to call super.onCreate().

Pierce answered 18/11, 2010 at 16:30 Comment(1)
Looks like Template method Design patternAdi
T
11

If you want to make absolutely sure that the superclass-method is called as well, you must trick a bit: Don't allow the superclass-method to be overwritten, but have it call an overridable protected method.

class Super
{
   public final void foo() {
      foo_stuff();
      impl_stuff();
   }

   protected void impl_stuff() {
      some_stuff_that_you_can_override();
   }
}

class Base extends Super
{
  protected void impl_stuff() { 
     my_own_idea_of_impl();
  }
}

That way, the user must call Super.foo() or Base.foo() and it will always be the base-class version as it was declared as final. The implementation-specific stuff is in impl_stuff(), which can be overriden.

Transference answered 18/11, 2010 at 16:29 Comment(0)
C
10

Here's the source of Activity#onCreate() - it is almost all comments (original - see line ~800):

/**
 * Called when the activity is starting.  This is where most initialization
 * should go: calling {@link #setContentView(int)} to inflate the
 * activity's UI, using {@link #findViewById} to programmatically interact
 * with widgets in the UI, calling
 * {@link #managedQuery(android.net.Uri , String[], String, String[], String)} to retrieve
 * cursors for data being displayed, etc.
 *
 * <p>You can call {@link #finish} from within this function, in
 * which case onDestroy() will be immediately called without any of the rest
 * of the activity lifecycle ({@link #onStart}, {@link #onResume},
 * {@link #onPause}, etc) executing.
 *
 * <p><em>Derived classes must call through to the super class's
 * implementation of this method.  If they do not, an exception will be
 * thrown.</em></p>
 *
 * @param savedInstanceState If the activity is being re-initialized after
 *     previously being shut down then this Bundle contains the data it most
 *     recently supplied in {@link #onSaveInstanceState}.  <b><i>Note: Otherwise it is null.</i></b>
 *
 * @see #onStart
 * @see #onSaveInstanceState
 * @see #onRestoreInstanceState
 * @see #onPostCreate
 */
protected void onCreate(Bundle savedInstanceState) {
    mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
            com.android.internal.R.styleable.Window_windowNoDisplay, false);
    mCalled = true;
}

so, my guess would be that the ADT Eclipse plugin is what's auto-adding that call to super.onCreate() for you. It's a total guess, though.

Caltrop answered 18/11, 2010 at 16:37 Comment(1)
I guess the mCalled = true also is used for a possible exception. Maybe not in the onCreate() but when indeed such a exception is thrown it will use that simple pattern.Monkeypot
V
8

To answer your actual question, the auto-creation of the call to super.onCreate() is a feature of the ADT plugin. In java, you cannot directly force a subclass to call the super implementation of a method, afaik (see the pattern described in other answers for work-around). However, keep in mind that in Android, you are not instantiating Activity objects (or Service objects) directly - you pass an Intent to the system and the system instantiates the object and calls onCreate() upon it (along with other lifecycle methods). So the system has a direct object reference to the Activity instance and is able to check (presumably) some Boolean that is set to true in the superclass implementation of onCreate(). Although I don't know exactly how it is implemented, it probably looks something like this:

class Activity
{
  onCreate()
  {
    superCalled = true;
    ...
  }
  ...
}

And in the "system" level class that receives the Intent and instantiates the Activity object from it:

...
SomeActivitySubclass someActivitySubclassObject = new SomeActivitySubclass();
someActivitySubclassObject.onCreate();
if (!someActivityObject.isSuperCalled())
{
  Exception e = new Exception(...) //create an exception with appropriate details
  throw e;
}

My guess is it's probably slightly more complex than that, but you get the idea. Eclipse automatically creates the call because the ADT plugin tells it to, as a convenience. Happy coding!

Vomer answered 27/6, 2012 at 23:8 Comment(0)
S
4

There is nothing in Java that forces calling of super, and there are plenty of examples when you wouldn't want to. The only place where you can force calling of super is in constructors. All constructors have to call a superclass constructor. One (the no arguments constructor) will be inserted if you don't write one explicitly, and if there is no no-arguments constructor then you must call it explicitly.

Sheri answered 18/11, 2010 at 16:22 Comment(0)
B
3

Eclipse is just being helpful, reminding you that you can call the superclass implementation if you want.

you are probably getting an error because you are not doing something necessary that the superclass does, since you are not calling its implementation.

Baudoin answered 18/11, 2010 at 16:22 Comment(0)
S
1

Eclipse just helps you doing things right and avoid exceptions.

From http://developer.android.com/reference/android/app/Activity.html#onCreate(android.os.Bundle)

Derived classes must call through to the super class's implementation of this method. If they do not, an exception will be thrown.

Seeder answered 17/3, 2011 at 0:1 Comment(0)
L
0

How does this happen? Is there a java keyword in the abstract or parent class that forces this?

As you observed, on Android calls to super() are auto-edited by IDEs when you override a function, and you get lint errors if you remove the call to super.

Methods can signal that methods which override them should call them by using the @CallSuper annotation. E.g., see the example given by the docs:

@CallSuper
override fun onCreate(savedInstanceState: Bundle?) {}

To use this, you may need to add the annotation library to your build.gradle:

dependencies {
    implementation("androidx.annotation:annotation:1.8.0")
}

Is this also built-in into java? Can you use some keyword to do that? Or how is it done?

It's not built into Java, but other answers here show manual approaches for plain Java.

If you are on Android, I recommend just using @CallSuper. It is "only" a linter error (not just a warning!). So if you run the linter at build time (which you should), the effect is the same: the build will fail if you don't call the super method.

Larousse answered 2/6 at 21:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.