Implemente Polimorfismo

O polimorfismo é um dos pilares fundamentais da Programação Orientada a Objetos (POO) e refere-se à capacidade de uma única interface ou método funcionar de diferentes maneiras para diferentes tipos de dados ou objetos. Em Java, o polimorfismo permite que um método ou uma classe tenha várias formas.

Existem dois tipos principais de polimorfismo em Java:

  1. Polimorfismo em Tempo de Compilação (Overloading)
  2. Polimorfismo em Tempo de Execução (Overriding)

Polimorfismo em Tempo de Compilação (Overloading)

O polimorfismo em tempo de compilação, ou sobrecarga de métodos, ocorre quando vários métodos em uma classe têm o mesmo nome, mas diferentes assinaturas (tipo e número de parâmetros).

Regras de Sobrecarga:

  1. Assinatura Diferente: Os métodos devem ter um número diferente de parâmetros ou tipos de parâmetros diferentes.
  2. Mesma Classe: A sobrecarga deve ocorrer dentro da mesma classe.
  3. Tipo de Retorno Não Importa: O tipo de retorno dos métodos sobrecarregados pode ser diferente, mas isso não é considerado na definição da sobrecarga.
  4. Modificadores de Acesso Podem Variar: Os métodos sobrecarregados podem ter diferentes modificadores de acesso (public, private, protected).

Exemplo de Sobrecarga de Métodos:

public class Calculadora {
    // Método para somar dois inteiros
    public int soma(int a, int b) {
        return a + b;
    }

    // Método para somar três inteiros
    public int soma(int a, int b, int c) {
        return a + b + c;
    }

    // Método para somar dois números de ponto flutuante
    public double soma(double a, double b) {
        return a + b;
    }
}

Quando vários métodos sobrecarregados estão presentes, o Java procura a correspondência mais próxima primeiro. Ele tenta encontrar o seguinte:

  1. Correspondência exata por tipo.
  2. Correspondência de um tipo de superclasse
  3. Convertendo para um tipo primitivo maior
  4. Convertendo para um tipo autoboxed
  5. Varargs

Polimorfismo em Tempo de Execução (Overriding)

O polimorfismo em tempo de execução, ou sobrescrita de métodos, ocorre quando uma classe derivada (subclasse) fornece uma implementação específica de um método que já é definido na sua classe base (superclasse). A sobrescrita permite que uma subclasse forneça uma implementação específica de um método que é já fornecido por uma de suas superclasses ou interfaces.

Regras de Sobrescrita:

  • Mesma Assinatura: O método sobrescrito deve ter exatamente a mesma assinatura que o método na superclasse (mesmo nome, tipo de retorno e parâmetros).
  • Tipo de Retorno Covariante: O tipo de retorno do método sobrescrito pode ser o mesmo ou um subtipo do tipo de retorno do método da superclasse.
  • Modificadores de Acesso: O método sobrescrito não pode ter um modificador de acesso mais restritivo do que o método na superclasse. Por exemplo, se o método na superclasse é public, o método sobrescrito não pode ser protected ou private.
  • Modificadores Especiais: Se o método na superclasse é marcado como final, static ou private, ele não pode ser sobrescrito.
  • Uso da Anotação @Override: Embora opcional, é uma boa prática usar a anotação @Override para indicar claramente que um método está sendo sobrescrito.
  • Exception: Se quaisquer exceções verificadas forem lançadas, somente as mesmas exceções ou subclasses dessas exceções poderão ser lançadas.

Exemplo de Sobrescrita de Métodos:

// Classe base
public class Animal {
    public void fazerSom() {
        System.out.println("O animal faz um som.");
    }
}

// Classe derivada
public class Cachorro extends Animal {
    @Override
    public void fazerSom() {
        System.out.println("O cachorro late.");
    }
}

// Classe derivada
public class Gato extends Animal {
    @Override
    public void fazerSom() {
        System.out.println("O gato mia.");
    }
}

Uso do Polimorfismo em Tempo de Execução

Podemos ver o poder do polimorfismo em tempo de execução ao usar referências da superclasse para apontar para objetos de subclasses e chamar métodos sobrescritos:

public class TestePolimorfismo {
    public static void main(String[] args) {
        Animal meuAnimal = new Animal();
        Animal meuCachorro = new Cachorro();
        Animal meuGato = new Gato();

        meuAnimal.fazerSom(); // Saída: O animal faz um som.
        meuCachorro.fazerSom(); // Saída: O cachorro late.
        meuGato.fazerSom(); // Saída: O gato mia.
    }
}

Vantagens do Polimorfismo

  1. Flexibilidade e Reusabilidade: Permite que o mesmo código trabalhe com diferentes tipos de dados e classes, aumentando a reusabilidade do código.
  2. Manutenibilidade: Facilita a manutenção e a expansão do código, permitindo que novas classes e métodos sejam adicionados com menos impacto no código existente.
  3. Extensibilidade: Novas funcionalidades podem ser adicionadas com facilidade sem modificar o código existente.

Diferenças entre Sobrecarga e Sobrescrita

  • Sobrecarga:
    • Ocorre dentro da mesma classe.
    • Os métodos têm o mesmo nome, mas diferentes assinaturas.
    • Pode ter diferentes tipos de retorno.
    • Pode ter diferentes modificadores de acesso.
  • Sobrescrita:
    • Ocorre entre uma superclasse e uma subclasse.
    • Os métodos têm exatamente a mesma assinatura.
    • O tipo de retorno deve ser o mesmo ou um subtipo (tipo de retorno covariante).
    • O método sobrescrito deve ter o mesmo ou um menos restritivo modificador de acesso.

Resumo

O polimorfismo em Java é uma poderosa característica que permite a criação de código mais flexível, reutilizável e extensível. Ele pode ser implementado através de sobrecarga de métodos (polimorfismo em tempo de compilação) e sobrescrita de métodos (polimorfismo em tempo de execução). Isso permite que métodos com o mesmo nome atuem de maneiras diferentes dependendo dos objetos ou parâmetros com os quais são usados.