Sobrescrita de método (ou overriding, em inglês) é um recurso da programação orientada a objetos, em que uma subclasse/classe filha redefine, ou seja, reescreve um método da superclasse/classe mãe ou da interface que implementa com a mesma assinatura, isto é: com o mesmo (1) nome, (2) tipo de retorno e (3) lista de parâmetros.
A ideia é que a classe filha possa fornecer uma implementação diferente do método da classe mãe para atender às suas necessidades específicas.
O método original da classe mãe continua existindo, mas a sua implementação é ignorada em favor da implementação da classe filha.
Em Java, a sobrescrita de método é feita adicionando a anotação @Override acima da declaração do método na classe filha. Além disso, a assinatura do método deve ser exatamente igual à da classe mãe. Veja um exemplo:
Arquivo Animal.java
public class Animal {
public void emitirSom() {
System.out.println("Som genérico de animal");
}
}
Arquivo Cachorro.java
public class Cachorro extends Animal {
@Override
public void emitirSom() {
System.out.println("Au au");
}
}
Nesse exemplo, a classe Cachorro herda o método emitirSom() da classe Animal, mas redefine/reescreve a sua implementação para emitir o som "Au au". Quando o método é chamado em um objeto do tipo Cachorro, a implementação da classe Cachorro é executada em vez da implementação da classe Animal.
Durante a execução de um programa Java com sobrescrita, a máquina virtual (JVM - Java Virtual Machine) é responsável por determinar qual versão do método deve ser executada com base no tipo de objeto invocado.
O compilador, por sua vez, é responsável por verificar se a sobrescrita está sendo feita corretamente, respeitando a assinatura do método original e o retorno esperado.
Sobrecarga de método é um recurso em que uma classe pode ter vários métodos com o mesmo nome, mas com parâmetros diferentes.
O compilador Java é capaz de diferenciar esses métodos pelo (1) quantidade, (2) tipo ou (3) ordem dos parâmetros, permitindo que um método com o mesmo nome tenha diferentes comportamentos, dependendo dos valores de entrada.
Arquivo Calculadora.java
public class Calculadora {
public int somar(int a, int b) {
return a + b;
}
public float somar(float a, float b) {
return a + b;
}
}
Por exemplo, podemos ter uma classe Calculadora com um método somar que recebe dois inteiros como parâmetros. Se quisermos adicionar suporte para somar floats também, podemos sobrecarregar o método somar, adicionando outro método com o mesmo nome que recebe dois floats como parâmetros. Dessa forma, quando chamamos o método somar com dois inteiros, o compilador irá escolher a primeira definição do método. Quando chamamos o método somar com dois floats, o compilador irá escolher a segunda definição do método.
A sobrecarga de método em Java é usada frequentemente em bibliotecas e frameworks, permitindo que as pessoas desenvolvedoras usem os mesmos nomes de método para comportamentos diferentes, tornando o código mais legível e intuitivo (quando bem documentado).
É possível sobrecarregar quaisquer métodos, inclusive o método construtor, desde que a lista de parâmetros seja diferente na quantidade de parâmetros ou na ordem dos tipos dos parâmetros.
public class Triangulo {
double area(int base, int altura){
return (base * altura) / 2;
}
double area(double base, double altura){
return (base * altura) / 2;
}
double area(int lado1, int lado2, int lado3, int raio){
return (lado1 * lado2 * lado3) / (4 * raio);
}
}
Neste exemplo, as versões do método área possuem parâmetros com quantidades e tipos diferentes.
public class Triangulo {
double area(int base, int altura){
return (base * altura) / 2;
}
double area(int altura, int base){
return (base * altura) / 2;
}
}
Neste exemplo, as versões do método área não atendem as regras para uma sobrecarga correta, pois possuem parâmetros iguais em quantidade e tipo. Este código não poderá ser compilado.
Tanto a sobrescrita de método, quanto a sobrecarga de método são recursos utilizados para fazer com que o comportamento (implementação) de um método possa variar. Entretanto, é importante lembrar algumas diferenças importantes entre as duas técnicas, que estão sumarizadas na tabela comparativa apresentada a seguir.
Arquivo Animal.java
public class Animal {
public void emitirSom() {
System.out.println("Som genérico de animal");
}
}
Arquivo Cachorro.java
public class Cachorro extends Animal {
@Override
public void emitirSom() {
System.out.println("Au au");
}
public String emitirSom(String tipoSom, int intensidade) {
String som = "O cachorro está emitindo o som " + tipoSom + " com intensidade " + intensidade;
System.out.println(som);
return som;
}
}
Arquivo Main.java
class Main {
public static void main(String[] args) {
Animal a01 = new Animal();
Cachorro c02 = new Cachorro();
a01.emitirSom();
c02.emitirSom();
c02.emitirSom("Grrrr", 5);
}
}
Saída da Console
Som genérico de animal
Au au
O cachorro está emitindo o som Grrrr com intensidade 5
Nesse exemplo, há a sobrescrita do método emitirSom() de Animal em Cachorro e a sobrecarga do método emitirSom em Cachorro.