Когда НЕ переписывать метод super() при переопределении?

Когда я создаю свой собственный пользовательский класс 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. Не связывайтесь с этим конкретным примером. Это был просто случай, случайно выбранный из многих примеров.

(Это может показаться новичком, но я действительно смущен.)

Ответ 1

Вызывая метод 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 для этих методов просто пуст, поэтому подклассы могут их переопределять и обеспечить поведение для этих методов. В javadoc для этого метода также указывается.

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

О вызове super() по умолчанию в коде, сгенерированном IDE, как указано в его комментарии в @barsju, в каждом конструкторе есть неявный вызов super() (даже если вы не записываете его в свой код), что означает в этом контексте вызов конструктора super по умолчанию. IDE просто записывает его для вас, но он также будет вызван, если вы его удалите. Также обратите внимание, что при реализации конструкторов super() или любой из его вариантов с аргументами (т.е. super(x,y,z)) можно вызывать только в самом начале метода.

Ответ 2

Как они узнают, когда вы должны называть супер и когда вы можете опустить вызов?

Обычно, если специальный метод API имеет критический смысл для жизненного цикла базового контекста, он всегда будет явно указан и выделен в документации API, например Activity.onCreate() Документация по API. Более того, если API следует за надежной конструкцией, он должен бросить некоторые исключения, чтобы предупредить разработчика-потребителя во время компиляции проекта и убедиться, что он не будет генерировать ошибку во время выполнения.

Если это явно не указано в документации API, то для разработчика-потребителя совершенно безопасно предположить, что метод API не обязательно вызывать при его переопределении. Разработчик потребителя может решить, использовать ли поведение по умолчанию (вызвать метод super) или полностью переопределить его.

Если условие разрешено (я люблю программное обеспечение с открытым исходным кодом), потребительский разработчик всегда может проверить исходный код API и посмотреть, как метод фактически написан под капотом. Проверьте Activity.onCreate() источник и DefaultHandler.startElement() source для пример.

Ответ 3

Тестирование, которое вы должны выполнить в своей голове:

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

Если вам нужны только некоторые функции (как и в большинстве методов, которые вы переопределите), вы, вероятно, не хотите вызывать super().

Ответ 4

Хорошо Xavi дал лучший ответ.. но вы, вероятно, знаете, что делает super() при вызове в переопределенном методе... он рекламирует, что вы сделали с поведением по умолчанию..

например:

onDraw() 

метод в классе вида при переопределении.. вы рисуете что-то перед тем, как сказать super.onDraw() появляется после полного рисования представления. поэтому здесь требуется вызов super, так как у android есть некоторые важные важные вещи (например, OnCreate())

но в то же время

onLongClick()

когда вы переопределяете это, вы не хотите называть супер, потому что он вызывает диалог со списком параметров для EditText или любого другого подобного вида. Это базовый diff.. у вас есть выбор, чтобы оставить его несколько раз.. но для других методов, таких как onCreate() , onStop(), вы должны позволить ОС обрабатывать его.

Ответ 5

Я не понял ваш вопрос четко, но если вы спрашиваете, почему не вызывать метод super:

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

Надеюсь, это поможет. Если нет, сообщите мне.

Ответ 6

Я реализовал список массивов ограничений, например

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

Если вы посмотрите на код, он просто выполняет некоторую предварительную проверку, прежде чем позволить супер классу выполнить фактическое добавление элемента в список. Это говорит о одной из двух кратных причин переопределения метода:

  • Расширяемость, в которой вы хотите расширить возможности суперкласса
  • Специфичность, в которой вы хотите добавить определенное поведение через полиморфизм, например, в общий пример царства животных в семантике перемещения, где перемещение птиц (перемещение) и перемещение лягушек (прыжок) зависят от каждого подкласса.