web-dev-qa-db-fra.com

Comment forcer une classe dérivée à appeler une super méthode? (Comme Android le fait)

Je me demandais, lors de la création de nouvelles classes Activity et de la substitution de la méthode onCreate(), dans Eclipse, je suis toujours ajouté automatiquement: super.onCreate(). Comment cela peut-il arriver? Y a-t-il un Java mot-clé dans la classe abstraite ou parent qui force cela?

Je ne sais pas s'il est illégal de ne pas appeler la super classe, mais je me souviens dans certaines méthodes que j'ai obtenu une exception pour ne pas avoir fait cela. Est-ce également intégré à Java? Pouvez-vous utiliser un mot clé pour le faire? Ou comment ça se passe?

74
Peterdk

Voici la source de Activity#onCreate() - c'est presque tous les commentaires ( original - voir ligne ~ 8 ):

/**
 * 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;
}

donc, je suppose que le plugin ADT Eclipse est ce qui ajoute automatiquement cet appel à super.onCreate() pour vous. C'est une supposition totale, cependant.

9
Matt Ball

Ceci est ajouté dans la bibliothèque d'annotations de support:

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

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

@CallSuper

169
runor49

Si vous souhaitez forcer les sous-classes pour exécuter la logique de la classe parent, un modèle courant ressemble à ceci:

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";
    }
}

Cela ne répond pas réellement à votre question sur ce qui incite Eclipse à insérer automatiquement un appel de superclasse dans l'implémentation; mais je ne pense pas que ce soit la voie à suivre de toute façon car cela peut toujours être supprimé.

Vous ne pouvez pas réellement imposer qu'une méthode doit appeler la version de la superclasse avec un mot-clé Java, ou quelque chose comme ça. Je soupçonne que vos exceptions proviennent simplement d'un code de la classe parent vérifiée attendue des invariants, ou quelque chose, qui ont été invalidés par votre approche. Notez que ceci est subtilement différent du lancement d'une exception car vous n'avez pas réussi à appeler super.onCreate().

79
Andrzej Doyle

Si vous voulez être absolument sûr que la méthode superclasse est également appelée, vous devez tromper un peu: n'autorisez pas la méthode superclasse à être écrasée, mais faites-la appeler une méthode protégée remplaçable.

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();
  }
}

De cette façon, l'utilisateur doit appeler Super.foo () ou Base.foo () et ce sera toujours la version de classe de base telle qu'elle a été déclarée finale. Le contenu spécifique à l'implémentation se trouve dans impl_stuff (), qui peut être remplacé.

8
Lagerbaer

Pour répondre à votre question réelle, la création automatique de l'appel à super.onCreate () est une fonctionnalité du plugin ADT. En Java, vous ne pouvez pas forcer directement une sous-classe à appeler la super implémentation d'une méthode, afaik (voir le modèle décrit dans d'autres réponses pour contourner le problème). Cependant, gardez à l'esprit que dans Android, vous n'instanciez pas directement des objets d'activité (ou des objets de service) - vous transmettez une intention au système et le système instancie l'objet et appelle onCreate () (ainsi que d'autres méthodes de cycle de vie). Ainsi, le système a une référence d'objet directe à l'instance Activity et est capable de vérifier (vraisemblablement) certains booléens qui sont définis sur true dans l'implémentation de superclasse de onCreate (). Bien que je ne sache pas exactement comment il est mis en œuvre, il ressemble probablement à ceci:

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

Et dans la classe de niveau "système" qui reçoit l'intention et en instancie l'objet activité:

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

Je suppose que c'est probablement un peu plus complexe que cela, mais vous voyez l'idée. Eclipse crée automatiquement l'appel parce que le plugin ADT le lui dit, par commodité. Bon codage!

8
speakingcode

Il n'y a rien dans Java qui force l'appel de super, et il y a plein d'exemples où vous ne voudriez pas. Le seul endroit où vous pouvez forcer l'appel de super est dans les constructeurs. Tous les constructeurs devez appeler un constructeur de superclasse. Un (le constructeur sans arguments) sera inséré si vous n'en écrivez pas explicitement, et s'il n'y a pas de constructeur sans arguments, vous devez l'appeler explicitement.

4
DJClayworth

Eclipse est juste utile, vous rappelant que vous pouvez appeler l'implémentation de superclasse si vous le souhaitez.

vous obtenez probablement une erreur parce que vous ne faites pas quelque chose de nécessaire que la superclasse, puisque vous n'appelez pas son implémentation.

3
hvgotcodes

Eclipse vous aide simplement à bien faire les choses et à éviter les exceptions.

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

Les classes dérivées doivent appeler l'implémentation de la super classe de cette méthode. S'ils ne le font pas, une exception sera levée.

1
Flygenring