Kiedy NIE wywoływać metody super () podczas nadpisywania?


113

Kiedy tworzę własną niestandardową klasę Androida, tworzę extendjej klasę natywną. Wtedy, kiedy chcę zastąpić metodę podstawową, zawsze wywołać super()metodę, tak jak zawsze w onCreate, onStopitp

Pomyślałem, że to jest to, ponieważ od samego początku zespół Android doradzał nam, aby zawsze wywoływać supernadpisanie każdej metody.

Ale w wielu książkach widzę, że programiści, bardziej doświadczeni ode mnie, często pomijają dzwonienie superi naprawdę wątpię, czy robią to z braku wiedzy. Na przykład, spójrz na tego podstawowego SAX klasy parsera w których superpominięto w startElement, charactersi 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
        }
    }
}

Jeśli spróbujesz utworzyć jakąkolwiek metodę nadpisywania za pośrednictwem Eclipse lub innego IDE, superzawsze zostanie utworzona jako część zautomatyzowanego procesu.

To był tylko prosty przykład. Książki są pełne podobnego kodu .

Skąd wiedzą, kiedy musisz zadzwonić, supera kiedy możesz pominąć dzwonienie?

PS. Nie wiąż się z tym konkretnym przykładem. To był tylko przykład losowo wybrany z wielu przykładów.

(To może brzmieć jak pytanie dla początkujących, ale jestem naprawdę zdezorientowany).


3
endElementDokument API mówi "Domyślnie nic nie rób. Twórcy aplikacji mogą przesłonić tę metodę ...", co oznacza, że ​​możesz bezpiecznie wywołać super, ponieważ nie robi "nic", ale nie musisz i naprawdę możesz to zmienić. Często możesz stwierdzić, czy musisz / nie możesz / nie powinieneś tego robić, czytając dokumentację dotyczącą tej metody.
zapl

1
Mały przykład: używam onBackPressed () na ekranie powitalnym i NIE wywołuję super, więc nie wychodzi z splasha, tylko wyłącza przycisk.
Bojan Kogoj

@sandalone Przepraszam, że pytam, ale czy masz jakieś doświadczenie w zakresie obsługi podatku VAT w przypadku rozliczeń w aplikacji oraz w jakich krajach jest to wykonywane automatycznie przez Google i do których muszę ręcznie zgłaszać / płacić podatek VAT? patrz stackoverflow.com/questions/36506835/…
Vidar Vestnes

Odpowiedzi:


144

Wywołując supermetodę, nie zastępujesz zachowania metody, tylko ją rozszerzasz .

Wywołanie superwykona dowolną logikę zdefiniowaną przez rozszerzaną klasę dla tej metody. Weź pod uwagę, że może to być ważny moment, w którym wywołasz superimplementację w swojej metodzie nadpisywania. Na przykład:

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

Wywołanie do B.save()wykona save()logikę dla obu Ai B, w tej określonej kolejności. Gdybyś nie dzwonił do super.save()środka B.save(), A.save()nie zostałbyś wezwany. A jeśli zadzwonisz super.save()później save(b), A.save()zostanie skutecznie wykonany później B.save().

Jeśli chcesz nadpisać super zachowanie (to znaczy całkowicie zignorować jego implementację i podać wszystko samodzielnie), nie powinieneś dzwonić super.

Na SAXParserprzykład podasz, tym implementacje z DefaultHandlertych metod są tylko pusty, dzięki czemu można je zastąpić podklasy i zapewnić zachowanie dla tych metod. W javadoc dla tej metody jest to również wskazane.

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

O super()domyślnym wywołaniu w kodzie generowanym przez IDE, jak @barsjuwskazano w jego komentarzu, w każdym konstruktorze znajduje się niejawne wywołanie super()(nawet jeśli nie piszesz go w swoim kodzie), co w tym kontekście oznacza wywołanie super' domyślny konstruktor. Po IDEprostu zapisuje go za Ciebie, ale zostanie również wywołany, jeśli go usuniesz. Zauważ również, że podczas implementacji konstruktorów super()lub któregokolwiek z jego wariantów z argumentami (tj. super(x,y,z)) Można wywołać tylko na samym początku metody.


14
Nie należy również pamiętać, że dla konstruktorów super()(domyślny konstruktor superklasy) jest zawsze wywoływana, nawet jeśli jej nie określisz. Jeśli superklasa nie ma domyślnego konstruktora, pojawi się błąd kompilacji, jeśli nie wywołasz jawnie jednego z konstruktorów superklasy jako pierwszej instrukcji w konstruktorze podklasy.
Barsju

1
Tak, to po prostu lenistwo - w przypadku metod, o których wiemy, nie rób nic, po prostu pomiń je dla zwięzłości
Voo

1
Dziękuję za cenne komentarze, @barjsu, zawarłem te szczegóły w odpowiedzi.
Xavi López

16

Skąd wiedzą, kiedy musisz zadzwonić super, a kiedy możesz pominąć dzwonienie?

Zwykle, jeśli specjalna metoda API ma krytyczne znaczenie dla cyklu życia podstawowego kontekstu frameworka, zawsze będzie to wyraźnie określone i wyróżnione w dokumentacji API, podobnie jak w Activity.onCreate()dokumentacji API . Ponadto, jeśli interfejs API jest zgodny z solidnym projektem, powinien zgłosić pewne wyjątki, aby ostrzec dewelopera konsumenta w czasie kompilacji projektu i upewnić się, że nie wygeneruje błędu w czasie wykonywania.

Jeśli nie jest to wyraźnie określone w dokumentacji API, wówczas deweloper-konsument może założyć, że wywołanie metody API nie jest obowiązkowe podczas jej zastępowania. Do dewelopera klienta należy decyzja, czy użyć domyślnego zachowania (wywołać supermetodę), czy całkowicie je zastąpić.

Jeśli warunek jest dozwolony (uwielbiam oprogramowanie typu open source), programista konsumencki zawsze może sprawdzić kod źródłowy API i zobaczyć, jak metoda jest faktycznie napisana pod maską. Sprawdź na przykład Activity.onCreate()źródło i DefaultHandler.startElement()źródło .


Dziękuję za wyjaśnienie mi dodatkowych rzeczy. Dziękuję za przypomnienie mi o sprawdzeniu kodu źródłowego.
sandalone

7

Test, który powinieneś zrobić w swojej głowie to:

„Czy chcę, aby cała funkcjonalność tej metody została wykonana za mnie, a potem coś zrobić?” Jeśli tak, to chcesz zadzwonić super(), a następnie dokończyć metodę. Odnosi się to do „ważnych” metod, takich jak onDraw(), które obsługują wiele rzeczy w tle.

Jeśli potrzebujesz tylko niektórych funkcji (jak w przypadku większości metod, które nadpisujesz), prawdopodobnie nie chcesz wywoływać super().


3

Cóż, Xavi udzielił lepszej odpowiedzi ... ale prawdopodobnie wiesz, co się super()dzieje, gdy wywoływana jest metoda z nadpisaniem ... reklamuje to, co zrobiłeś z domyślnym zachowaniem.

na przykład:

onDraw() 

metoda w klasie widoku, gdy jest zastępowana .. rysujesz coś przed powiedzeniem super.onDraw () pojawia się, gdy widok jest w pełni narysowany .. więc tutaj wywołanie superjest konieczne, ponieważ android ma kilka krytycznie ważnych rzeczy do zrobienia (np. onCreate ())

ale w tym samym czasie

onLongClick()

kiedy to nadpisujesz, nie chcesz wywoływać super, ponieważ pojawia się okno dialogowe z listą opcji dla EditText lub innego podobnego widoku .. To jest podstawowa różnica .. możesz zostawić to czasami .. ale dla inne metody, takie jak onCreate() , onStop()powinieneś pozwolić systemowi operacyjnemu obsłużyć.


2

Nie otrzymałem jasno twojego pytania, ale jeśli pytasz, dlaczego nie wywołać supermetody:

Istnieje powód wywołania supermetody: jeśli w klasie nadrzędnej nie ma konstruktora zeroargumentowego, nie jest możliwe utworzenie dla tego klasy potomnej, więc albo musisz zachować konstruktor bez argumentów w klasie nadrzędnej, albo potrzebujesz do zdefiniowania super()instrukcji wywołującej argument(how much argument constructor you have used in super class)na początku konstruktora klasy potomnej.

Mam nadzieję, że to pomoże. Jeśli nie, daj mi znać.


2

Zaimplementowałem listę tablic ograniczeń, taką jak

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

Jeśli spojrzysz na kod, po prostu wykonuje pewne wstępne sprawdzenie, zanim faktycznie pozwoli superklasie wykonać faktyczne dodanie elementu do listy. To mówi o jednym z dwóch powodów przesłaniania metody:

  1. Rozszerzalność tam, gdzie chcesz rozszerzyć możliwości superklasy
  2. Specyfika, w której chcesz dodać określone zachowanie poprzez polimorfizm, tak jak w typowym przykładzie semantyki ruchu w królestwie zwierząt, w którym sposób poruszania się ptaków (latanie) i żab (przeskakiwanie) jest specyficzny dla każdej podklasy.

2

Dla tych, którzy również zastanawiali się, które zastąpione metody z Android Framework powinny wywołać superi znaleźli to pytanie - oto aktualna wskazówka z 2019 roku - Android Studio 3+ powie Ci, kiedy tego potrzebujesz .

wprowadź opis obrazu tutaj

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.