W3docs

Palavra-chave super em Java

Referencie a classe pai em Java com super — chamando construtores e métodos sobrescritos da classe pai.

super é o equivalente de this para a classe pai. Onde this é "o objeto atual", super é "o objeto atual, visto como sua superclasse." Só faz sentido dentro de uma classe que estende outra, e aparece em três lugares:

  1. super(args) dentro de um construtor — chama um construtor da classe pai.
  2. super.method(args) dentro de um método de instância — chama a versão da classe pai de um método sobrescrito.
  3. super.field dentro de um método de instância — lê um campo da classe pai que a subclasse oculta.

O primeiro é, de longe, o mais comum.

super(...) — chamando um construtor da classe pai

Todo construtor de subclasse deve começar chamando algum construtor da classe pai. Se você não escrever nenhum, o Java insere super(); como primeira linha — uma chamada ao construtor sem argumentos da classe pai:

public class Animal {
  String name;
  public Animal() { name = "(unnamed)"; }     // no-arg
}

public class Cat extends Animal {
  public Cat() {
    // super();  ← inserted by the compiler
  }
}

Quando a classe pai não possui construtor sem argumentos, você precisa chamar um explicitamente:

public class Animal {
  String name;
  public Animal(String name) { this.name = name; }   // no no-arg constructor
}

public class Cat extends Animal {
  public Cat(String name) {
    super(name);              // required — there is no Animal()
  }
}
Aviso

O erro mais comum em herança é esquecer isso. Quando uma classe pai não tem construtor sem argumentos, o compilador ainda tenta inserir super(); em qualquer construtor de subclasse que não chame super(...) explicitamente — e então falha com "there is no default constructor available in Animal". A correção é adicionar uma chamada explícita super(...) que corresponda a um construtor real da classe pai.

Regras para super(...):

  • Deve ser a primeira instrução no corpo do construtor.
  • Um construtor pode encadear para outro construtor da mesma classe com this(...) ou chamar um pai com super(...) — não ambos.
  • Os argumentos devem corresponder a um construtor existente da classe pai.

Por que a regra de "primeira instrução"?

A JVM precisa construir completamente a parte da classe pai do objeto antes que qualquer código da subclasse seja executado. Isso inclui inicializar campos final que a classe pai declara. Permitir que o código da subclasse seja executado primeiro significaria ler esses campos antes que lhes fossem atribuídos seus valores.

super.method(args) — chamando a versão da classe pai

Dentro de um método de instância, super.method(...) chama a versão da classe pai de method, mesmo que a classe atual o tenha sobrescrito:

public class Animal {
  String speak() { return "(some noise)"; }
}

public class Cat extends Animal {
  @Override
  String speak() {
    return super.speak() + " — actually, meow";
    //     ^^^^^^^^^^^^^ calls Animal.speak(), not Cat.speak()
  }
}

new Cat().speak();    // "(some noise) — actually, meow"

Sem super., um simples speak() dentro de Cat.speak chamaria a si mesmo e entraria em recursão infinita.

O padrão clássico é estender o comportamento da classe pai em vez de substituí-lo:

@Override
void onSave() {
  super.onSave();      // run parent's save logic first
  // then add subclass-specific behavior
}

super.method(...) não pode alcançar dois níveis acima — não existe super.super.method() em Java. Se sua classe estende uma classe que estende outra, você pode chamar o método do seu pai imediato, não do seu avô.

super.field — acessando além de um campo oculto

Se uma subclasse declara um campo com o mesmo nome que o campo de uma classe pai, o campo da subclasse oculta o da classe pai:

public class A {
  String label = "A's label";
}
public class B extends A {
  String label = "B's label";

  void show() {
    System.out.println(label);          // B's label
    System.out.println(super.label);    // A's label
    System.out.println(this.label);     // B's label
  }
}

Isso quase sempre é um design ruim — ter dois campos com o mesmo nome em um par pai-filho é confuso — mas a linguagem fornece super.field para disambiguar quando isso acontece. A correção mais limpa é renomear um dos campos.

Note que campos não são polimórficos. Ao contrário dos métodos sobrescritos, o acesso a campos é decidido em tempo de compilação com base no tipo declarado da referência. O capítulo sobre polimorfismo explica o porquê.

super e static

super só existe em contextos de instância, como this. Você não pode usar super em um método ou inicializador static — não há objeto atual, portanto não há visão da classe pai dele. Para chamar um método estático declarado na classe pai, qualifique-o com o nome da classe pai:

public class A { static void hi() { System.out.println("hi"); } }
public class B extends A {
  static void demo() {
    A.hi();             // ok — call by class name
    // super.hi();      // ERROR — super in a static context
  }
}

Uma subclasse também pode declarar um método static com o mesmo nome que o método static de uma classe pai — isso é chamado de ocultação de método, não sobrescrita, e segue regras de tempo de compilação. Isso é abordado brevemente no capítulo de sobrescrita de método.

Cadeias de super

Cada chamada super(...) ativa a próxima camada da cadeia de herança. Rastreando o que é executado para uma subclasse profundamente aninhada:

new SiameseCat("Lulu")
  → SiameseCat(String)
      → super("Lulu")        — Cat(String)
          → super("Lulu")    — Animal(String)
              → super()      — Object()
              → Animal body runs
          → Cat body runs
      → SiameseCat body runs

A inicialização sempre prossegue do ancestral mais distante até a classe atual. Quando o corpo de SiameseCat é executado, todos os campos da classe pai estão completamente inicializados.

Um exemplo prático

java— editable, runs on the server

O que vem a seguir

super é a ponte para a classe pai, mas a ideia maior que ele possibilita é o polimorfismo — a capacidade de chamar um método em uma referência tipada como pai e ter o despacho para a implementação correta da subclasse em tempo de execução. Esse é o próximo capítulo. Continue para Polimorfismo em Java.

Prática

Prática
Dentro de um construtor, quando o compilador insere implicitamente super();?
Dentro de um construtor, quando o compilador insere implicitamente super();?
Was this page helpful?