web-dev-qa-db-fra.com

Quand NE PAS appeler la méthode super () lors d'une substitution?

Lorsque je crée ma propre classe personnalisée Android, je extend sa classe native. Ensuite, lorsque je souhaite redéfinir la méthode de base, j'appelle toujours la méthode super() , comme je le fais toujours dans onCreate, onStop, etc.

Et je pensais que c’était ça, depuis le tout début Android nous a conseillé de toujours appeler super à chaque substitution de méthode.

Mais, dans de nombreux livres , je constate que les développeurs, plus expérimentés que moi, omettent souvent d'appeler super et je doute vraiment qu'ils le fassent. comme un manque de connaissance. Par exemple, regardez cette base SAX classe d'analyse où super est omis dans startElement, characters et endElement:

public class SAXParser extends DefaultHandler{
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        if(qName.equalsIgnoreCase("XXY")) {
            //do something
        }
    }

    public void characters(char[] ch, int start, int length) throws SAXException {
        //do something
    }

    public void endElement(String uri, String localName, String qName) throws SAXException {
        if(qName.equalsIgnoreCase("XXY")) {
            //do something
        }else () {
            //do something
        }
    }
}

Si vous essayez de créer une méthode de remplacement via Eclipse ou tout autre IDE, super sera toujours créé dans le cadre du processus automatisé.

Ce n'était qu'un exemple simple. Les livres sont pleins de code similaire .

Comment savent-ils quand vous devez appeler super et quand vous pouvez l'omettre?

PS Ne vous liez pas à cet exemple spécifique. Ce n'était qu'un exemple choisi au hasard parmi de nombreux exemples.

(Cela peut sembler une question de débutant, mais je suis vraiment confus.)

109
sandalone

En appelant la méthode super, vous n'êtes pas annulant le comportement de la méthode, vous êtes l’étendant .

Un appel à super exécutera toute logique définie par la classe que vous étendez pour cette méthode. Tenez compte du fait que le moment où vous appelez l'implémentation de super dans votre méthode doit être important. Par exemple:

public class A { 
    public void save() { 
         // Perform save logic
    }
}

public class B extends A {
    private Object b;
    @Override
    public void save() { 
        super.save(); // Performs the save logic for A
        save(b); // Perform additional save logic
    }
}

Un appel à B.save() exécutera la logique save() pour A et B, dans cet ordre particulier. Si vous n'appeliez pas super.save() à l'intérieur de B.save(), A.save() ne serait pas appelé. Et si vous appeliez super.save() après save(b), A.save() serait effectivement exécuté par la suite B.save().

Si vous voulez remplacer le comportement de super (c'est-à-dire ignorer entièrement son implémentation et fournir tout cela vous-même), vous ne devriez pas être appelant super.

Dans l'exemple SAXParser que vous fournissez, les implémentations de DefaultHandler pour ces méthodes sont tout simplement vides, de sorte que les sous-classes peuvent les remplacer et fournir un comportement à ces méthodes. Dans le javadoc , cette méthode est également indiquée.

public void startElement (String uri, String localName,
    String qName, Attributes attributes) throws SAXException {
    // no op
}

A propos de l'appel super() default dans le code généré par les IDE, comme l'a souligné @barsju Dans son commentaire, il existe dans chaque constructeur un appel implicite à super() (même si vous ne l’écrivez pas dans votre code), ce qui signifie, dans ce contexte, un appel au constructeur par défaut de super. Le IDE l'écrit simplement pour vous, mais il sera également appelé si vous le supprimez. Notez également que lors de l’implémentation de constructeurs, super() ou l’une de ses variantes avec arguments (c.-à-d. super(x,y,z)) ne peut être appelée qu'au tout début de la méthode.

140
Xavi López

Comment savent-ils quand vous devez appeler super et quand vous pouvez l'omettre?

Généralement, si une méthode API spéciale a une signification critique pour le cycle de vie du contexte de la structure sous-jacente, elle sera toujours explicitement indiquée et mise en surbrillance dans la documentation de l’API, comme le Activity.onCreate() documentation de l’API . De plus, si l’API suit une conception robuste, elle devrait émettre quelques exceptions pour alerter le développeur consommateur au moment de la compilation du projet et s’assurer qu’elle ne générera pas d’erreur au moment de l’exécution.

Si cela n'est pas explicitement indiqué dans la documentation de l'API, le développeur consommateur peut donc supposer que la méthode de l'API n'est pas obligatoire à appeler lors de son remplacement. Il appartient au développeur consommateur de décider s'il souhaite utiliser le comportement par défaut (appeler la méthode super) ou le remplacer complètement.

Si la condition est autorisée (j'aime les logiciels open-source), le développeur grand public peut toujours consulter le code source de l'API et voir comment la méthode est réellement écrite sous le capot. Vérifier Activity.onCreate() source et DefaultHandler.startElement() source par exemple.

16
yorkw

Le test que vous devriez faire dans votre tête est:

"Est-ce que je veux que toutes les fonctionnalités de cette méthode soient faites pour moi, puis que je fasse quelque chose par la suite?" Si oui, alors vous voulez appeler super(), puis terminer votre méthode. Cela sera vrai pour les méthodes "importantes" telles que onDraw(), qui gère beaucoup de choses en arrière-plan.

Si vous souhaitez uniquement utiliser certaines fonctionnalités (comme pour la plupart des méthodes à remplacer), vous ne souhaitez probablement pas appeler super().

7
Michael

Eh bien Xavi a donné une meilleure réponse .. mais vous pourriez probablement savoir ce que fait super() quand il est appelé dans une méthode surchargée ... il annonce ce que vous avez fait avec le comportement par défaut ..

par exemple:

onDraw() 

méthode dans la classe de vue en cas de substitution .. vous dessinez quelque chose avant de dire super.onDraw (), il apparaît une fois que la vue est entièrement dessinée .. alors, ici, appeler super est nécessaire depuis Android a des choses très importantes à faire (comme onCreate ())

mais en même temps

onLongClick()

lorsque vous annulez ceci, vous ne souhaitez pas appeler super car il ouvre une boîte de dialogue avec une liste d’options pour un EditText ou toute autre vue similaire .. C’est le diff de base .. vous avez le choix de le laisser quelques fois .. mais pour d’autres méthodes comme onCreate() , onStop(), vous devriez laisser le système d’exploitation le gérer.

3
ngesh

J'ai mis en place une liste de contraintes comme

public class ConstraintArrayList<T> extends ArrayList<T> {
  ConstraintArrayList(Constraint<T> cons) {this.cons = cons;}
  @Override
  public boolean add(T element) {
    if (cons.accept(element))
      return super.add(element);
    return false;
  }
}

Si vous examinez le code, il effectue simplement une vérification préalable avant de laisser la super classe effectuer l'ajout réel d'élément à la liste. Cela indique l’une des deux raisons de l’abandon de la méthode:

  1. Extensibilité où vous voulez étendre ce que la super classe peut faire
  2. Spécificité pour laquelle vous souhaitez ajouter un comportement spécifique par le biais du polymorphisme, comme dans l'exemple courant du règne animal de la sémantique des déplacements, dans lequel la façon dont les oiseaux se déplacent (mouches) et les grenouilles se déplacent (sauts) est spécifique à chaque sous-classe.
2
maress

Pour ceux qui se demandaient également quelles méthodes remplacées par Android Framework devrait appeler super et trouver cette question - voici un indice actuel datant de 2019 - Android Studio 3+ vous dira quand vous en aurez besoin .

enter image description here

2
dominik

Je n’ai pas bien compris votre question, mais si vous demandez pourquoi ne pas appeler la méthode super:

Il y a une raison pour appeler la méthode super: s'il n'y a pas de constructeur à argument zéro dans la classe parente, il n'est pas possible de créer une classe enfant pour cela, vous devez donc conserver un constructeur sans argument dans la classe parente ou vous devez définir une instruction d'appel super() avec argument(how much argument constructor you have used in super class) en haut du constructeur de la classe enfant.

J'espère que ça aide. Si non, faites le moi savoir.

2
Vikas Gupta