Коли НЕ викликати метод super () при переопределенні?


113

Коли я роблю власний власний клас Android, я extendйого рідний клас. Тоді, коли я хочу змінити базовий метод, я завжди викликаю super()метод, як я завжди це роблю onCreate, onStopі т.д.

І я подумав, що це все, тому що з самого початку команда Android радила нам завжди закликати superкожен метод замінити.

Але в багатьох книгах я бачу, що розробники, більш досвідчені, ніж я, часто пропускають дзвінки, superі я дуже сумніваюся, що вони роблять це як брак знань. Наприклад, подивіться на цей базовий клас аналізатора SAX, де superвін пропущений startElement, charactersі 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
        }
    }
}

Якщо ви спробуєте створити будь-який метод заміщення за допомогою Eclipse або будь-якого іншого IDE, superвін завжди створюватиметься як частина автоматизованого процесу.

Це був просто простий приклад. Книги рясніють подібним кодом .

Звідки вони знають, коли потрібно дзвонити superі коли ви можете пропустити дзвінок?

PS. Не пов'язуйте цей конкретний приклад. Це був лише приклад, випадковим чином обраний із багатьох прикладів.

(Це може здатися питанням для початківців, але я дуже розгублений.)


3
endElementAPI doc каже: "За замовчуванням нічого не робити. Автори програм можуть перекрити цей метод ..." Це означає, що ви можете сміливо викликати супер, тому що він робить "нічого", але вам цього не потрібно, і ви дійсно можете його перекрити. Ви часто можете сказати, чи потрібно / не можете / не варто цього робити, якщо читаєте документ для цього методу.
zapl

1
Невеликий приклад: я використовую onBackPress () на екрані заставки, і я НЕ дзвоню супер, щоб він не вийшов із сплеску, він просто відключає кнопку.
Bojan Kogoj

@sandalone Вибачте за запитання, але чи є у вас досвід роботи з податковим податком на ПДВ за допомогою виставлення рахунків через додаток, і в яких країнах це робиться автоматично через Google, і яким я повинен вручну звітувати / сплачувати податок з ПДВ? см stackoverflow.com/questions/36506835 / ...
Відар Vestnes

Відповіді:


144

Викликавши superметод, ви не перекриваючи поведінку методу, ви розширення його.

Виклик superвиконуватиме будь-яку логіку, який клас, який ви розширюєте, визначив для цього методу. Враховуйте, що це може бути важливим моментом, коли ви зателефонуєте superна реалізацію в своєму методі переосмислення. Наприклад:

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

Заклик до B.save()буде виконувати save()логіку для обох, Aі Bв цьому конкретному порядку. Якщо ви не дзвонилиsuper.save() всередину B.save(), A.save()не дзвонили б. І якщо ви назвали super.save()після того save(b), A.save()будуть ефективно виконуватися після B.save().

Якщо ви хочете змінити super поведінку (тобто повністю ігнорувати її реалізацію та забезпечити все це самостійно), вам не слід телефонувати super.

У наданому SAXParserприкладі реалізації з DefaultHandlerза ці методи є просто пустими, так що підкласи можуть мати найвищий пріоритет їх і забезпечити поведінку для цих методів. У Javaдоці для цього методу також зазначено.

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

Щодо super()виклику за замовчуванням у коді, згенерованому IDE, як @barsjuзазначено у його коментарі, у кожному конструкторі є неявний виклик super()(навіть якщо ви не записуєте його у своєму коді), що означає у цьому контексті дзвінок на super' s конструктор за замовчуванням. IDEПросто записує його для вас, але це також додзвонилися , якщо ви видалили його. Також зауважте, що при реалізації конструкторів super()або будь-яких його варіантів з аргументами (тобто super(x,y,z)) можна викликати лише на самому початку методу.


14
Також не зауважте, що для конструкторів super()( конструктор за замовчуванням суперкласу) завжди викликається, навіть якщо ви його не вказуєте. Якщо в суперкласі немає конструктора за замовчуванням, ви отримаєте помилку компіляції, якщо явно не викличете одного з конструкторів суперкласу як свого першого виразу в конструкторі підкласу.
barsju

1
Так, це в основному лише лінь - для методів, які ми знаємо, нічого не роблять, просто пропускаємо це для стислості
Voo

1
Дякуємо за ваші цінні коментарі, @barjsu, я включив ці деталі у відповідь.
Хаві Лопес

16

Звідки вони знають, коли потрібно дзвонити супер і коли ви можете опустити це дзвінок?

Зазвичай, якщо спеціальний метод API має критичне значення для основного життєвого циклу контексту, він завжди буде чітко зазначено та виділено в документації API, як-от Activity.onCreate()документація API . Більше того, якщо API дотримується надійної конструкції, він повинен нанести деякі винятки, щоб попередити споживача-розробника під час складання проекту та переконатися, що він не призведе до помилок під час виконання програми.

Якщо це прямо не зазначено в документації API, то споживчий розробник цілком безпечно вважати, що метод API не є обов'язковим для виклику при його перезаписі. Розробник споживача повинен вирішити, чи використовувати поведінку за замовчуванням (зателефонуйте наsuper метод) або повністю її відміняти.

Якщо умова дозволена (я люблю програмне забезпечення з відкритим кодом), розробник споживачів завжди може перевірити вихідний код API і побачити, як метод насправді записаний під кришкою. Наприклад, ознайомтесь із Activity.onCreate()джерелом та DefaultHandler.startElement()джерелом .


Дякуємо, що пояснили мені додаткові речі. Дякуємо, що нагадали мені перевірити вихідний код.
сандалон

7

Тест, який ви повинні зробити в своїй голові:

"Я хочу, щоб вся функціональність цього методу була зроблена для мене, а потім зробити щось після цього?" Якщо так, то ви хочете зателефонувати super(), а потім закінчити свій метод. Це буде справедливо для "важливих" методів, таких якonDraw() обробка багатьох речей у фоновому режимі.

Якщо ви хочете лише функціонувати (як у більшості методів, які ви перекриєте), ви, ймовірно, не хочете телефонувати super().


3

Ну, Хаві дав кращу відповідь .. але ви, напевно, можете знати, що super()робити, коли викликається в переосмисленому методі ... він оголошує, що ви зробили з поведінкою за замовчуванням ..

наприклад:

onDraw() 

метод у класі перегляду при переосмисленні .. ви щось намалюєте перед тим, як сказати super.onDraw (), це з’являється, коли вигляд повністю намальовано .. тому тут виклик superнеобхідний, оскільки в android є деякі критично важливі речі (наприклад, onCreate ())

але в той же час

onLongClick()

коли ви переосмислите це, ви не хочете дзвонити супер, тому що він відкриває діалогове вікно із переліком параметрів для EditText або будь-якого іншого подібного перегляду .. Ось основний розл., ви можете залишити його кілька разів .. але для інші методи, такі як onCreate() , onStop()ви повинні дозволити ОС це впоратися ..


2

Я не зрозумів ваше запитання чітко, але якщо ви запитуєте, чому не викликати superметод:

Існує причина для дзвінка на super методу: якщо в батьківському класі немає конструктора нульових аргументів, для цього неможливо скласти дочірній клас, тому або потрібно зберегти конструктор аргументів у батьківському класі, або вам потрібно визначити super()оператор виклику з argument(how much argument constructor you have used in super class)у верхній частині конструктора дочірнього класу.

Я сподіваюся, що це допомагає. Якщо ні, то дайте мені знати.


2

Я реалізував подібний список масивів обмежень

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

Якщо ви подивитеся на код, він просто виконує попередню перевірку, перш ніж фактично дозволить суперкласу виконати фактичне додавання елемента до списку. Це говорить про одну з двох кращих причин переосмислення методу:

  1. Розширюваність, де ви хочете розширити те, що може зробити суперклас
  2. Особливості, де ви хочете додати специфічну поведінку через поліморфізм, наприклад, у загальному прикладі тваринного царства, наприклад, семантиці руху, де шлях птахів (літаючих) та жаб (хоп) є специфічним для кожного підкласу.

2

Для тих, хто також замислювався над тим, які переосмислені методи з Android Framework повинні зателефонувати, superі знайшов це питання - ось поточний підказку з 2019 року - Android Studio 3+ підкаже, коли вам це потрібно .

введіть тут опис зображення

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.