Quando NÃO chamar o método super () ao substituir?

113

Quando eu faço minha própria classe personalizada Android, eu uso extendsua classe nativa. Então, quando eu quiser substituir o método base, eu sempre chamo super()método, assim como eu sempre faço em onCreate, onStop, etc.

E eu pensei que era isso, desde o início a equipe do Android nos aconselhou a sempre chamar supertodos os métodos de substituição.

Mas, em muitos livros , posso ver que os desenvolvedores, mais experientes do que eu, muitas vezes omitem a chamada supere eu realmente duvido que eles façam isso por falta de conhecimento. Por exemplo, olhe para isto básica SAX classe do analisador, onde superé omitido na 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 você tentar criar qualquer método de substituição via Eclipse ou qualquer outro IDE, supersempre será criado como parte do processo automatizado.

Este foi apenas um exemplo simples. Os livros estão cheios de códigos semelhantes .

Como eles sabem quando você deve ligar supere quando pode omitir a ligação?

PS. Não se vincule a este exemplo específico. Foi apenas um exemplo escolhido aleatoriamente entre muitos exemplos.

(Pode parecer uma pergunta de iniciante, mas estou muito confuso.)

sandalone
fonte
3
endElementO documento da API de diz "Por padrão, não faça nada. Os criadores de aplicativos podem sobrescrever este método ...", o que significa que você pode chamar super com segurança porque ele não faz "nada", mas você não precisa e pode realmente sobrescrevê-lo. Freqüentemente, você pode dizer se precisa / pode / não deve fazer isso se ler o documento desse método.
zapl
1
Pequeno exemplo: Eu uso onBackPressed () na tela inicial e NÃO chamo super para que ele não saia da tela inicial, apenas desativa o botão.
Bojan Kogoj
@sandalone Desculpe por perguntar aqui, mas você tem alguma experiência em como lidar com o imposto de IVA com faturamento no aplicativo, em quais países isso é feito automaticamente pelo Google e para quais devo informar / pagar manualmente o imposto de IVA? consulte stackoverflow.com/questions/36506835/…
Vidar Vestnes

Respostas:

144

Ao chamar o supermétodo, você não está substituindo o comportamento do método, está estendendo -o.

Uma chamada para superexecutará qualquer lógica que a classe que você está estendendo tenha definido para esse método. Leve em consideração que pode ser importante o momento em que você chama supera implementação na substituição de seu método. Por exemplo:

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

Uma chamada para B.save()executará a save()lógica para Ae B, nesta ordem particular. Se você não ligasse para super.save()dentro B.save(), A.save()não seria chamado. E se você ligasse super.save()depois save(b), A.save()seria efetivamente executado depois B.save().

Se você deseja sobrescrever super o comportamento de (ou seja, ignorar totalmente sua implementação e fornecer tudo sozinho), você não deve chamar super.

No SAXParserexemplo que você fornece, as implementações de DefaultHandlerpara esses métodos estão simplesmente vazias, de modo que as subclasses podem substituí-las e fornecer um comportamento para esses métodos. No javadoc para esse método, isso também é indicado.

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

Sobre a super()chamada padrão no código gerado por IDEs, conforme @barsjuapontado em seu comentário, em cada construtor há uma chamada implícita para super()(mesmo que você não escreva em seu código), o que significa, nesse contexto, uma chamada para super' s construtor padrão. Ele IDEapenas anota para você, mas também seria chamado se você o removesse. Observe também que, ao implementar construtores, super()ou qualquer uma de suas variantes com argumentos (ou seja, super(x,y,z)) só podem ser chamados no início do método.

Xavi López
fonte
14
Também não note que for constructors super()(o construtor padrão da superclasse) é sempre chamado, mesmo que você não o especifique. Se a superclasse não tiver um construtor padrão, você obterá um erro de compilação se não chamar explicitamente um dos construtores da superclasse como sua primeira instrução no construtor da subclasse.
barsju de
1
Sim, é basicamente apenas preguiça - para métodos que sabemos não fazer nada, apenas omita por brevidade
Voo
1
Obrigado por seus valiosos comentários, @barjsu, incluí esses detalhes na resposta.
Xavi López
16

Como eles sabem quando você deve ligar para super e quando pode omitir a ligação?

Normalmente, se um método de API especial tem um significado crítico para o ciclo de vida do contexto da estrutura subjacente, ele sempre será declarado explicitamente e destacado na documentação da API, como a Activity.onCreate()documentação da API . Além disso, se a API segue um design robusto, deve lançar algumas exceções para alertar o desenvolvedor consumidor no tempo de compilação do projeto e garantir que não gere uma falha no tempo de execução.

Se isso não for declarado explicitamente na documentação da API, é bastante seguro para o desenvolvedor do consumidor assumir que o método da API não é obrigatório para chamar ao substituí-lo. Cabe ao desenvolvedor consumidor decidir se deve usar o comportamento padrão (chamar o supermétodo) ou substituí-lo completamente.

Se a condição for permitida (adoro software de código aberto), o desenvolvedor consumidor pode sempre verificar o código-fonte da API e ver como o método está realmente escrito nos bastidores. Verifique a Activity.onCreate()fonte e a DefaultHandler.startElement()fonte, por exemplo.

yorkw
fonte
Obrigado por explicar coisas adicionais para mim. Obrigado por me lembrar de verificar o código-fonte.
sandalone
7

O teste que você deve fazer em sua cabeça é:

"Eu quero que todas as funcionalidades deste método sejam feitas para mim e, em seguida, faço algo depois?" Se sim, então você deseja chamar super()e, em seguida, finalizar seu método. Isso será verdadeiro para métodos "importantes" como onDraw(), que manipula muitas coisas em segundo plano.

Se você deseja apenas algumas das funcionalidades (como a maioria dos métodos que você substituirá), provavelmente não deseja chamar super().

Michael
fonte
3

Bem, Xavi deu uma resposta melhor .. mas você provavelmente deve saber o que super()faz quando chamado em um método sobrescrito ... ele mostra o que você fez com o comportamento padrão ..

por exemplo:

onDraw() 

método na classe de visualização quando substituído .. você desenha algo antes de dizer super.onDraw (), ele aparece quando a visualização é totalmente desenhada .. então aqui superé necessário chamar uma vez que o Android tem algumas coisas criticamente importantes a fazer (como onCreate ())

mas ao mesmo tempo

onLongClick()

quando você sobrescreve isto, você não quer chamar super porque abre uma caixa de diálogo com uma lista de opções para um EditText ou qualquer outra visão semelhante .. Essa é a diferença básica .. você tem a opção de deixá-la algumas vezes .. mas por outros métodos como onCreate() , onStop()você deve deixar o sistema operacional lidar com isso.

ngesh
fonte
2

Não entendi claramente sua pergunta, mas se você está perguntando por que não chamar o supermétodo:

Há uma razão para chamar o supermétodo: se não houver nenhum construtor de argumento zero na classe pai, então não é possível fazer uma classe filha para isso, então você precisa manter um construtor sem argumento na classe pai ou você precisa para definir a super()instrução de chamada com argument(how much argument constructor you have used in super class)no topo do construtor da classe filha.

Eu espero que isso ajude. Se não, me avise.

Vikas Gupta
fonte
2

Eu implementei uma lista de matriz de restrição como

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 você olhar o código, ele simplesmente fará uma pré-verificação antes de realmente permitir que a superclasse execute a adição real do elemento à lista. Isso mostra um dos dois motivos para a substituição do método:

  1. Extensibilidade onde você deseja estender o que a superclasse pode fazer
  2. Especificidade onde você deseja adicionar um comportamento específico por meio de polimorfismo, como no exemplo comum do reino animal de semântica de movimento, em que a maneira como os pássaros se movem (voam) e os sapos se movem (pulam) são específicos para cada subclasse.
maress
fonte
2

Para aqueles que também se perguntaram quais métodos substituídos do Android Framework deveriam ser chamados supere encontraram esta pergunta - aqui está uma dica atual de 2019 - o Android Studio 3+ dirá quando você precisar .

insira a descrição da imagem aqui

Dominik
fonte