1. Usando super para chamar métodos da classe base
Ao criar uma subclasse, às vezes surge a necessidade de acessar campos ou métodos da classe base, especialmente se você os sobrescreveu ou os “ocultou” (shadowing) na subclasse. Para isso, em Java existe a palavra‑chave especial — super.
Fazendo uma analogia, super é como “mãe, me ajuda!”, quando, na subclasse, você quer se referir explicitamente ao que está definido no pai.
Imagine que você tenha a classe Animal com o método eat(), que apenas imprime "O animal come". E na classe Cat você quer que o gato primeiro faça algo próprio (por exemplo, miar) e, em seguida, ainda execute o padrão de “animal comendo”. É aí que entra super.eat().
Quando, na subclasse, você sobrescreve um método, mas ainda assim quer chamar dentro dele a implementação desse método da classe base, use super.nomeDoMetodo().
Exemplo: estendendo o comportamento
class Animal {
void eat() {
System.out.println("O animal come");
}
}
class Cat extends Animal {
@Override
void eat() {
System.out.println("O gato cheira a comida...");
super.eat(); // chamamos o método eat() de Animal
System.out.println("O gato ronrona satisfeito");
}
}
Como isso funciona?
- Quando eat() é chamado em um objeto do tipo Cat, primeiro é executado o código de Cat.eat(), ou seja, o método próprio.
- Dentro desse método, chamamos explicitamente super.eat(), isto é, a implementação da classe pai Animal.
- Isso permite adicionar comportamento extra sem esquecer o “do pai”.
Prática: usando no aplicativo
Suponha que em nosso aplicativo de estudos exista a classe base Animal e as subclasses Dog e Cat. Queremos que, ao alimentar o animal, sejam executadas tanto ações comuns (por exemplo, aumentar a saciedade) quanto as específicas de cada animal.
class Animal {
int satiety = 0;
void eat() {
satiety += 10;
System.out.println("O animal come. Saciedade: " + satiety);
}
}
class Dog extends Animal {
@Override
void eat() {
System.out.println("O cachorro abana o rabo antes de comer");
super.eat();
}
}
Agora, se você chamar dog.eat(), verá as duas mensagens, e satiety será incrementado corretamente.
2. Usando super para acessar campos da classe base
Se, na subclasse, você declarar um campo com o mesmo nome do da classe pai, ele “oculta” o campo do pai. Às vezes é preciso acessar o campo original da classe base — para isso serve super.nomeDoCampo.
Exemplo: ocultação de campo
class Animal {
String name = "Animal";
}
class Cat extends Animal {
String name = "Gato";
void printNames() {
System.out.println("Nome de Cat: " + name);
System.out.println("Nome de Animal: " + super.name);
}
}
A chamada new Cat().printNames(); imprimirá:
Nome de Cat: Gato
Nome de Animal: Animal
Na prática, “ocultar” campos não é recomendado, a menos que seja estritamente necessário, mas vale conhecer essa possibilidade.
3. Chamada do construtor da classe base via super(...)
Como os objetos são criados em uma hierarquia?
Ao criar um objeto de subclasse, primeiro é chamado o construtor da classe base e só depois o da subclasse. Isso é necessário para que todos os campos sejam inicializados corretamente, já que a subclasse “herda” parte do estado do pai.
Chamada explícita do construtor da classe base
Se a classe base tiver um construtor sem parâmetros, é simples: o Java o chamará antes de executar o construtor da subclasse. Mas, se o pai não tiver construtor sem parâmetros, você é obrigado a chamar explicitamente o construtor adequado via super(...).
Exemplo:
class Animal {
String name;
Animal(String name) {
this.name = name;
System.out.println("Animal criado: " + name);
}
}
class Cat extends Animal {
Cat(String name) {
super(name); // obrigatório! Não há construtor Animal() sem parâmetros
System.out.println("Gato criado: " + name);
}
}
A chamada new Cat("Murka") imprimirá:
Animal criado: Murka
Gato criado: Murka
Importante: A chamada ao construtor do pai via super(...) deve ser a primeira linha do construtor da subclasse. Se você tentar escrever algo antes dessa chamada, o compilador reclamará e lembrará você disso.
E se não chamar explicitamente?
Se o pai tiver apenas construtor com parâmetros e você não o chamar explicitamente via super(...), o compilador emitirá um erro: "constructor Animal in class Animal cannot be applied to given types".
4. Nuances úteis
Quando usar super?
Para estender, não substituir o comportamento.
Às vezes você não quer substituir completamente o comportamento de um método, apenas “estendê-lo”. Por exemplo, adicionar algo antes ou depois da lógica do pai. Nesses casos, use super.nomeDoMetodo() no corpo do método sobrescrito.
Para inicializar campos herdados.
Se o pai tiver campos obrigatórios para inicialização (por exemplo, o nome do animal), chame o construtor do pai com os parâmetros necessários via super(...).
Para acessar campos/métodos ocultos.
Se, por algum motivo, você “ocultou” um campo ou método do pai e ainda precisa acessá-lo, use super.nomeDoCampo ou super.nomeDoMetodo().
Restrições e particularidades do uso de super
- A chamada do construtor do pai via super(...) só pode ser feita no construtor e apenas como a primeira linha.
- Não é possível chamar o construtor do pai fora do construtor da subclasse.
- Se você não chamar super(...) explicitamente, o Java tentará chamar o construtor sem parâmetros do pai (se existir).
- A palavra‑chave super não pode ser usada em métodos estáticos — apenas em métodos de instância (não estáticos) e em construtores.
- Se o método ou campo do pai for private, super não ajudará: não há acesso a membros privados.
5. Exemplos para fixação
Exemplo 1. Estendendo um método com super
class Animal {
void makeSound() {
System.out.println("O animal emite um som");
}
}
class Dog extends Animal {
@Override
void makeSound() {
super.makeSound(); // primeiro faz a ação padrão
System.out.println("O cachorro late: Au-au!");
}
}
Exemplo 2. Chamada do construtor da classe base
class Vehicle {
String brand;
Vehicle(String brand) {
this.brand = brand;
System.out.println("Veículo: " + brand);
}
}
class Car extends Vehicle {
int year;
Car(String brand, int year) {
super(brand); // chamamos o construtor do pai
this.year = year;
System.out.println("Carro " + brand + ", ano: " + year);
}
}
Car car = new Car("Toyota", 2023);
// Saída:
// Veículo: Toyota
// Carro Toyota, ano: 2023
Exemplo 3. Erro clássico: esqueceu de chamar super(...)
class Animal {
String name;
Animal(String name) {
this.name = name;
}
}
class Cat extends Animal {
Cat() {
// super(); // Erro! Não há construtor Animal() sem parâmetros
// É preciso chamar explicitamente super(name)
super("Gato sem nome");
}
}
6. Erros comuns ao trabalhar com super
Erro nº 1: chamar super(...) não como a primeira linha do construtor.
O Java exige estritamente que a chamada ao construtor do pai via super(...) seja a primeira linha do construtor da subclasse. Se você tentar fazer algo antes dessa chamada (por exemplo, imprimir uma mensagem), o compilador emitirá um erro.
Erro nº 2: não existe um construtor adequado no pai.
Se a classe base não tiver construtor sem parâmetros e você não chamar outro construtor via super(...), o compilador não conseguirá gerar a chamada padrão e informará um erro.
Erro nº 3: tentativa de acessar membros privados do pai via super.
A palavra‑chave super não dá acesso mágico a campos ou métodos privados do pai. Se algo for declarado como private, permanece inacessível para a subclasse.
Erro nº 4: ocultar campos e métodos sem compreender as implicações.
Se você declarou na subclasse um campo ou método com o mesmo nome do do pai e se esqueceu disso, resultados inesperados podem ocorrer. Lembre-se de que, nesse caso, o acesso ao membro do pai é apenas via super.
Erro nº 5: uso de super em método estático.
Em métodos estáticos não é possível usar super, porque eles não pertencem a um objeto específico.
GO TO FULL VERSION