CodeGym /Cursos /JAVA 25 SELF /Sobrescrita de métodos (overriding): diferença em relação...

Sobrescrita de métodos (overriding): diferença em relação à sobrecarga

JAVA 25 SELF
Nível 18 , Lição 2
Disponível

1. Sobrescrita de métodos

Sobrescrita (overriding) — é a possibilidade de escrever, em uma subclasse, sua própria versão de um método que já existe na classe pai. Graças a isso, funciona o polimorfismo real em tempo de execução (run-time).

Em palavras simples:
Se você tem uma classe base com um método e quer que a subclasse execute esse método do seu próprio jeito, basta declarar um método com a mesma assinatura na subclasse. Ao chamar o método por meio de uma referência do tipo base, será chamada a versão do método do tipo real do objeto.

Exemplo do dia a dia.
Imagine que você tem um grupo de animais e pede a cada um “emitir um som”. Para todos, o comando é makeSound(), mas o cachorro late, o gato mia e a vaca muge. No código, isso parece a chamada do mesmo método, mas o resultado é diferente — eis a magia da sobrescrita!

Sintaxe da sobrescrita

Exemplo básico

class Animal {
    void makeSound() {
        System.out.println("O animal emite um som");
    }
}

class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("Au!");
    }
}

class Cat extends Animal {
    @Override
    void makeSound() {
        System.out.println("Miau!");
    }
}

Aqui, na classe Animal, está definido o método makeSound(). As subclasses Dog e Cat sobrescrevem esse método, fornecendo sua própria implementação.

Anotação @Override

Em Java, é recomendado (e é um bom hábito) marcar métodos sobrescritos com a anotação @Override:

@Override
void makeSound() { ... }

Isso não é obrigatório para o código funcionar, mas:

  • O compilador verificará se você realmente está sobrescrevendo um método (e não digitou errado o nome ou os parâmetros por engano).
  • Melhora a legibilidade do código — outros programadores veem imediatamente que esse método sobrescreve o do pai.

Chamada de um método sobrescrito

Animal myDog = new Dog();
myDog.makeSound(); // Irá imprimir: Au!

Embora a variável seja do tipo Animal, na prática ela aponta para um objeto Dog, e o método chamado é o da classe Dog. Isso é vinculação dinâmica (tardia).

2. Diferença entre sobrescrita (overriding) e sobrecarga (overloading)

Sobrecarga (overloading)

  • Na mesma classe (ou dentro da hierarquia, mas sempre “perto”).
  • Os métodos têm o mesmo nome, mas parâmetros diferentes (tipo, quantidade, ordem).
  • A escolha do método ocorre em tempo de compilação.
void print(int x) { ... }
void print(String s) { ... }

Sobrescrita (overriding)

  • Em classes diferentes: o método é declarado na superclasse e sobrescrito na subclasse.
  • Os métodos têm o mesmo nome e assinatura (parâmetros e tipo de retorno).
  • A escolha do método ocorre em tempo de execução (run-time), com base no tipo real do objeto.

Tabela comparativa

Sobrecarga (overloading) Sobrescrita (overriding)
Onde Na mesma classe Na superclasse e na subclasse
Nome do método Igual Igual
Parâmetros Diferentes Iguais
Tipo de retorno Pode ser diferente Deve corresponder ou ser um subtipo
Quando é escolhido Compilação Execução (run-time)
Anotação Não é necessário @Override (recomendado)

3. Regras de sobrescrita de métodos

Sobrescrever é algo poderoso, mas vem com regras rígidas. Vamos analisá-las em ordem.

A assinatura do método deve coincidir

  • O nome do método, os tipos e a ordem dos parâmetros devem ser idênticos aos do método na superclasse.
  • O tipo de retorno deve coincidir ou ser covariante (ou seja, um subtipo do tipo de retorno do método da superclasse).

Exemplo com tipo de retorno covariante:

class Animal {
    Animal reproduce() { return new Animal(); }
}
class Cat extends Animal {
    @Override
    Cat reproduce() { return new Cat(); } // OK! Cat é um subtipo de Animal
}

Modificador de acesso

O modificador de acesso do método sobrescrito não pode ser mais restritivo do que o do método na superclasse. Se o método no pai é public, então na subclasse ele deve continuar public. Não é permitido torná-lo menos acessível (protected ou private).

Exemplo:

class Parent {
    public void greet() { }
}
class Child extends Parent {
    // void greet() { } // Erro! O modificador padrão — package-private — é menos acessível do que public
    @Override
    public void greet() { } // OK
}

Exceções

  • Um método sobrescrito não pode lançar novas exceções checked que não estejam declaradas no método base.
  • Pode lançar menos ou as mesmas exceções.

Exemplo:

class Parent {
    void doWork() throws IOException { }
}
class Child extends Parent {
    @Override
    void doWork() throws FileNotFoundException { } // OK, FileNotFoundException é um subtipo de IOException
    // void doWork() throws SQLException { } // Erro! SQLException não está declarado no pai
}

Métodos estáticos não são sobrescritos

Métodos estáticos podem ser escondidos (hidden), mas não sobrescritos. Se você declarar um método estático com a mesma assinatura na subclasse, isso não é sobrescrita! Será apenas ocultação do método, e não polimorfismo.

class Animal {
    static void info() { System.out.println("Animal"); }
}
class Dog extends Animal {
    static void info() { System.out.println("Dog"); }
}

A chamada Dog.info() imprimirá "Dog", mas, se chamar por meio de uma variável do tipo Animal, será chamado o método Animal.info(). Isso não é polimorfismo!

final-métodos não podem ser sobrescritos

Se um método na superclasse for declarado como final, tentar sobrescrevê-lo resultará em erro de compilação.

class Animal {
    final void sleep() { }
}
class Dog extends Animal {
    // @Override
    // void sleep() { } // Erro! Não é possível sobrescrever um método final
}

4. Exemplos práticos

Vamos ver na prática como a sobrescrita funciona e como ela se diferencia da sobrecarga.

Exemplo 1: classe Shape e suas subclasses

class Shape {
    void draw() {
        System.out.println("Desenhando uma figura");
    }
}

class Circle extends Shape {
    @Override
    void draw() {
        System.out.println("Desenhando um círculo");
    }
}

class Rectangle extends Shape {
    @Override
    void draw() {
        System.out.println("Desenhando um retângulo");
    }
}

Usando polimorfismo:

public class Main {
    public static void main(String[] args) {
        Shape s1 = new Circle();
        Shape s2 = new Rectangle();
        s1.draw(); // Desenhando um círculo
        s2.draw(); // Desenhando um retângulo
    }
}

Embora as variáveis sejam declaradas como Shape, o método chamado é o da classe à qual o objeto realmente pertence.

Exemplo 2: Diferença em relação à sobrecarga

class Printer {
    void print(String s) {
        System.out.println("String: " + s);
    }

    void print(int n) {
        System.out.println("Número: " + n);
    }
}

Aqui, ambos os métodos se chamam print, mas têm parâmetros diferentes — isso é sobrecarga, não sobrescrita.

5. Sobrescrita e chamada do método da classe pai (super)

Às vezes, no método sobrescrito, você quer primeiro executar a lógica do pai e depois adicionar a sua. Para isso, usa-se a palavra-chave super.

class Animal {
    void makeSound() {
        System.out.println("O animal emite um som");
    }
}

class Dog extends Animal {
    @Override
    void makeSound() {
        super.makeSound(); // Chamada do método da classe pai
        System.out.println("Au!");
    }
}

A chamada new Dog().makeSound() imprimirá:

O animal emite um som
Au!

Como funciona a vinculação dinâmica (late binding)

Quando você chama um método por meio de uma referência do tipo base, o Java, em tempo de execução, verifica a que objeto real essa referência aponta e chama exatamente a versão do método que está definida na classe desse objeto.

Animal a = new Cat();
a.makeSound(); // Chamará Cat.makeSound(), e não Animal.makeSound()

Essa é a base do polimorfismo em Java.

6. Como isso se relaciona ao seu aplicativo

No nosso aplicativo de aprendizado (por exemplo, um sistema de gestão de funcionários), você pode criar a classe base Employee com o método work(), e as subclasses Manager e Developer podem implementar esse método à sua maneira:

class Employee {
    void work() {
        System.out.println("O funcionário trabalha");
    }
}

class Manager extends Employee {
    @Override
    void work() {
        System.out.println("O gerente lidera");
    }
}

class Developer extends Employee {
    @Override
    void work() {
        System.out.println("O desenvolvedor escreve código");
    }
}

Agora você pode armazenar todos os funcionários em um único array ou lista:

Employee[] employees = {new Manager(), new Developer(), new Developer()};
for (Employee e : employees) {
    e.work(); // Para cada um — sua própria saída!
}

7. Erros comuns ao sobrescrever métodos

Erro nº 1: erro de digitação no nome do método ou nos parâmetros. Se você se enganou no nome do método ou em seus parâmetros, você não está sobrescrevendo o método, e sim criando um novo. Como resultado, o polimorfismo não funciona. Por isso, sempre use a anotação @Override — o compilador vai corrigir você na hora.

Erro nº 2: modificador de acesso mais restritivo. Se no pai o método é public e, na classe filha, você o declarar como protected ou sem modificador — ocorrerá um erro de compilação.

Erro nº 3: tentar sobrescrever um método static ou final. Métodos estáticos não são sobrescritos, e métodos final simplesmente não podem ser sobrescritos. Se você tentar — o compilador irá barrar.

Erro nº 4: alterar o tipo de retorno para um incompatível. Se o tipo de retorno do método na subclasse não coincide com o da superclasse (e não é um subtipo dele), o compilador não permitirá sobrescrever o método.

Erro nº 5: adicionar novas exceções checked. Um método sobrescrito não pode lançar novas exceções checked que não estejam na declaração do método base. Se fizer isso — o compilador emitirá um erro.

Erro nº 6: esquecer o super. Se, no método sobrescrito, você quiser preservar parte do comportamento do pai, não esqueça de chamar explicitamente super.methodName(). O Java não fará isso automaticamente.

Agora você sabe como a sobrescrita de métodos funciona, como ela difere da sobrecarga e como ela viabiliza o polimorfismo em Java. Na próxima aula, veremos como aplicar o polimorfismo na prática — com coleções, arrays e tarefas reais!

1
Tarefa
JAVA 25 SELF, nível 18, lição 2
Bloqueado
Vozes do zoológico: Sobrescrita de método na subclasse
Vozes do zoológico: Sobrescrita de método na subclasse
1
Tarefa
JAVA 25 SELF, nível 18, lição 2
Bloqueado
Mágica da impressão: Diferença entre sobrescrita e sobrecarga
Mágica da impressão: Diferença entre sobrescrita e sobrecarga
1
Tarefa
JAVA 25 SELF, nível 18, lição 2
Bloqueado
A arte de desenhar: Uso da anotação @Override
A arte de desenhar: Uso da anotação @Override
1
Tarefa
JAVA 25 SELF, nível 18, lição 2
Bloqueado
Apresentação musical do pet: Invocando o método da classe pai usando super
Apresentação musical do pet: Invocando o método da classe pai usando super
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION