Quando NON chiamare il metodo super () durante l'override?


113

Quando creo la mia classe personalizzata Android, ho la extendsua classe nativa. Poi quando voglio l'override del metodo di base, ho sempre chiamare super()il metodo, proprio come faccio sempre in onCreate, onStopecc

E ho pensato che fosse così, poiché sin dall'inizio il team di Android ci ha consigliato di chiamare sempre superl'override di ogni metodo.

Ma in molti libri posso vedere che gli sviluppatori, più esperti di me, spesso omettono di chiamare supere dubito davvero che lo facciano per mancanza di conoscenza. Per esempio, guardate questa base SAX classe di parser in cui superviene omesso in startElement, characterse 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
        }
    }
}

Se provi a creare un metodo di sostituzione tramite Eclipse o qualsiasi altro IDE, superverrà sempre creato come parte del processo automatizzato.

Questo era solo un semplice esempio. I libri sono pieni di codice simile .

Come fanno a sapere quando devi chiamare supere quando puoi ometterlo di chiamare?

PS. Non legarti a questo esempio specifico. Era solo un esempio scelto a caso da molti esempi.

(Potrebbe sembrare una domanda per principianti, ma sono davvero confuso.)


3
endElementIl documento API di dice "Per impostazione predefinita, non fare nulla. Gli autori di applicazioni possono sovrascrivere questo metodo ...", il che significa che puoi tranquillamente chiamare super perché non fa "niente" ma non devi e puoi davvero sovrascriverlo. Spesso puoi dire se devi / puoi / non dovresti farlo se leggi il documento per quel metodo.
zapl

1
Piccolo esempio: uso onBackPressed () nella schermata iniziale e NON chiamo super, quindi non va fuori splash, disabilita semplicemente il pulsante.
Bojan Kogoj

@sandalone Scusa per averlo chiesto qui, ma hai qualche esperienza su come gestire l'imposta IVA con la fatturazione in-app e in quali paesi viene eseguita automaticamente da Google e a cui devo dichiarare / pagare manualmente l'imposta IVA? vedi stackoverflow.com/questions/36506835/…
Vidar Vestnes

Risposte:


144

Chiamando il supermetodo, non stai sovrascrivendo il comportamento del metodo, lo stai estendendo .

Una chiamata a supereseguirà qualsiasi logica definita dalla classe che stai estendendo per quel metodo. Tieni presente che potrebbe essere importante il momento in cui chiami superl'implementazione nel tuo metodo che sovrascrive. Per esempio:

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

Una chiamata a B.save()eseguirà la save()logica per entrambi Ae B, in questo ordine particolare. Se non chiamassi super.save()dentro B.save(), A.save()non verrebbero chiamati. E se chiamassi super.save()dopo save(b), A.save()verrebbe effettivamente eseguito in seguito B.save().

Se vuoi sovrascrivere super il comportamento di (cioè, ignorare completamente la sua implementazione e fornire tutto da solo), non dovresti chiamare super.

Nel SAXParseresempio si forniscono, le implementazioni di DefaultHandlerper quei metodi sono solo vuote, in modo che le sottoclassi li possono sostituire e fornire un comportamento per quei metodi. Nel javadoc di questo metodo viene anche sottolineato questo.

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

Riguardo alla super()chiamata predefinita nel codice generato dagli IDE, come @barsjusottolineato nel suo commento, in ogni costruttore c'è una chiamata implicita a super()(anche se non la scrivi nel tuo codice), che significa, in quel contesto, una chiamata a super' s costruttore predefinito. Lo IDEscrive solo per te, ma verrebbe chiamato anche se lo rimuovi. Notare anche che quando si implementano i costruttori, super()o qualsiasi delle sue varianti con argomenti (cioè super(x,y,z)), può essere chiamato solo all'inizio del metodo.


14
Inoltre, non notare che per i costruttori super()(il costruttore predefinito della super classe) viene sempre chiamato, anche se non lo specifichi. Se la super classe non ha un costruttore predefinito, si otterrà un errore di compilazione se non si chiama esplicitamente uno dei costruttori della super classe come prima istruzione nel costruttore della sottoclasse.
barsju

1
Sì, è fondamentalmente solo pigrizia - per i metodi che sappiamo non fare nulla, omettilo per brevità
Voo

1
Grazie per i tuoi preziosi commenti, @barjsu, ho incluso questi dettagli nella risposta.
Xavi López

16

Come fanno a sapere quando devi chiamare super e quando puoi ometterlo di chiamare?

Di solito, se un metodo API speciale ha un significato critico per il ciclo di vita del contesto del framework sottostante, sarà sempre esplicitamente dichiarato ed evidenziato nella documentazione API, come la Activity.onCreate()documentazione API . Inoltre, se l'API segue un design robusto, dovrebbe generare alcune eccezioni per avvisare lo sviluppatore consumer al momento della compilazione del progetto e assicurarsi che non generi un errore in fase di esecuzione.

Se ciò non è esplicitamente indicato nella documentazione API, è abbastanza sicuro per lo sviluppatore consumer presumere che il metodo API non sia obbligatorio da chiamare quando lo sovrascrive. Spetta allo sviluppatore consumer decidere se utilizzare il comportamento predefinito (chiamare il supermetodo) o ignorarlo completamente.

Se la condizione è consentita (adoro il software open source), lo sviluppatore consumer può sempre controllare il codice sorgente dell'API e vedere come il metodo è effettivamente scritto sotto il cofano. Controlla Activity.onCreate()fonte e DefaultHandler.startElement()fonte per esempio.


Grazie per avermi spiegato altre cose. Grazie per avermi ricordato di controllare il codice sorgente.
sandalo

7

Il test che dovresti fare nella tua testa è:

"Voglio che tutte le funzionalità di questo metodo vengano eseguite per me e poi faccia qualcosa?" Se sì, allora vuoi chiamare super()e poi completare il tuo metodo. Questo sarà vero per metodi "importanti" come onDraw(), che gestisce molte cose in background.

Se vuoi solo alcune delle funzionalità (come con la maggior parte dei metodi che sovrascrirai), probabilmente non vorrai chiamare super().


3

Ebbene Xavi ha dato una risposta migliore .. ma probabilmente saprai cosa fa super()quando viene chiamato in un metodo sovrascritto ... annuncia cosa hai fatto con il comportamento predefinito ..

per esempio:

onDraw() 

metodo nella classe di visualizzazione quando viene sovrascritto .. disegni qualcosa prima di dire super.onDraw () appare una volta che la vista è completamente disegnata .. quindi qui la chiamata superè necessaria poiché Android ha alcune cose di fondamentale importanza da fare (come onCreate ())

ma allo stesso tempo

onLongClick()

quando sovrascrivi questo non vuoi chiamare super perché fa apparire una finestra di dialogo con un elenco di opzioni per un EditText o qualsiasi altra vista simile .. Questa è la differenza di base .. puoi scegliere di lasciarla alcune volte .. ma per altri metodi come onCreate() , onStop()te dovrebbero lasciare che il sistema operativo lo gestisca ..


2

Non ho capito chiaramente la tua domanda, ma se stai chiedendo perché non chiamare il supermetodo:

C'è una ragione per chiamare il supermetodo: se non c'è un costruttore di argomenti zero nella classe genitore, non è possibile creare una classe figlia per quello, quindi o è necessario mantenere un costruttore senza argomenti nella classe genitore o è necessario per definire l' super()istruzione di chiamata con argument(how much argument constructor you have used in super class)all'inizio del costruttore della classe figlia.

Spero che aiuti. In caso contrario, fammi sapere.


2

Ho implementato un elenco di array di vincoli come

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

Se guardi il codice, esegue semplicemente un controllo preliminare prima di consentire effettivamente alla super classe di eseguire l'effettiva aggiunta di elementi all'elenco. Questo spiega uno dei due motivi per cui il metodo ignora:

  1. Estensibilità dove si desidera estendere ciò che può fare la super classe
  2. Specificità in cui si desidera aggiungere un comportamento specifico attraverso il polimorfismo come nell'esempio comune della semantica del movimento del regno animale in cui il modo in cui gli uccelli si muovono (volano) e le rane si muovono (saltellano) sono specifici per ogni sottoclasse.

2

Per coloro che si sono anche chiesti quali metodi ignorati da Android Framework dovrebbero chiamare supere hanno trovato questa domanda - ecco un suggerimento attuale dal 2019 - Android Studio 3+ ti dirà quando ne hai bisogno .

inserisci qui la descrizione dell'immagine

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.