Programação Orientada a Objetos – Java

Polimorfismo

Prof. José Rodolfo Beluzo

O que é Polimorfismo?

Polimorfismo é a capacidade de um mesmo método comportar-se de maneiras diferentes dependendo do objeto que o executa.

  • Métodos iguais → comportamentos diferentes
  • Objetos diferentes podem ser tratados por um mesmo tipo (classe pai ou interface)
  • Acontece em tempo de execução (dynamic dispatch)

Exemplo motivador


Animal a = new Cachorro();
a.fazerSom(); // "Au au!"

Animal b = new Gato();
b.fazerSom(); // "Miau!"
  

A variável é do tipo Animal, mas o comportamento depende do objeto real.

Polimorfismo de Sobrescrita (Override)

Uma classe filha redefine um método da classe pai.


class Animal {
    public void fazerSom() { System.out.println("Som genérico..."); }
}

class Cachorro extends Animal {
    @Override
    public void fazerSom() {. System.out.println("Au au!");  }
}

class Gato extends Animal {
    @Override
    public void fazerSom() { System.out.println("Miau!"); }
}
  

Chamadas polimórficas


Animal a1 = new Cachorro();
Animal a2 = new Gato();

a1.fazerSom(); // Au au!
a2.fazerSom(); // Miau!
  

Uma mesma referência (Animal), vários comportamentos.

Exemplo Resolvido – Cálculo de Frete (Polimorfismo)


// Classe base
abstract class Frete {
  public abstract double calcular(double peso, 
                              double distancia);
}
    

// Implementações
class Sedex extends Frete {
    public double calcular(double peso, double distancia) {
        return peso * 1.2 + distancia * 0.5;
    }
}

class Loggi extends Frete {
    public double calcular(double peso, double distancia) {
        return peso * 0.9 + distancia * 0.7;
    }
}

class Motoboy extends Frete {
    public double calcular(double peso, double distancia) {
        return 15.0; // tarifa fixa
    }
}
    

//Classe principal
public class App {
    public static void main(String[] args) throws Exception {
        Frete s = new Sedex();

        Frete l = new Loggi();

        Venda v1 = new Venda(3,s);
        
        Venda v2 = new Venda(3,l);

        v1.calcularVenda(0.5,50);
        
        v2.calcularVenda(0.5,50);


        v1.exibeValorFinal();

        v2.exibeValorFinal();

    }
}



    

Explicação: Cada classe calcula o frete de forma diferente, mas todas podem ser usadas pelo mesmo método. Se surgir nova transportadora, basta criar outra classe, sem alterar o código da Loja.

Exercício Prático – Polimorfismo em Sistema de Pagamentos

Imagine um sistema de e-commerce que aceita diferentes formas de pagamento. O método processar() deve funcionar para qualquer tipo de pagamento, sem alterar o código principal do sistema.

Tarefa:

  1. Crie uma classe abstrata Pagamento com o método abstrato processar(double valor).
  2. Implemente pelo menos três tipos de pagamento:
    • PagamentoCartao – exiba mensagem de processamento no cartão e aplica 5% de taxa no valor.
    • PagamentoPix – exiba mensagem de processamento do PIX com 5% de desconto do valor.
    • PagamentoBoleto – exiba mensagem de pagamento no valor original.
  3. Crie uma classe Caixa com um método pagar() que receba um objeto do tipo Pagamento e chame processar().
  4. No método main, teste chamadas com diferentes formas de pagamento.

// exemplo de execução esperada:

Caixa caixa = new Caixa();

caixa.pagar(new PagamentoCartao(), 250); //Pagamento no Cartão: Valor final - R$262.50

caixa.pagar(new PagamentoPix(), 250); // Pagamento no Pix: Valor Final - R$237.50

caixa.pagar(new PagamentoBoleto(), 250); // Pagamento no Boleto: Valor Final - R$250.00

  

Interfaces: contratos de comportamento

  • Definem o que uma classe deve fazer, não como.
  • Uma classe pode implementar várias interfaces.
  • Podem ter default e static methods.
  • Facilitam o polimorfism.
interface Conduzivel { void mover(); }
interface Abastecivel { void abastecer(double litros); }
class Carro implements Conduzivel, Abastecivel {
  @Override public void mover(){ System.out.println("Acelerando..."); }
  @Override public void abastecer(double l){ /* ... */ }
}

Polimorfismo com Interfaces

Quando diferentes classes implementam a mesma interface, e podem ser tratadas por ela.


interface Forma {
    double area();
}

class Quadrado implements Forma {
    public double area() {
        return 25;
    }
}

class Circulo implements Forma {
    public double area() {
        return 12.56;
    }
}
  

Chamando polimorficamente


Forma f = new Quadrado();
System.out.println(f.area()); // 25

f = new Circulo();
System.out.println(f.area()); // 12.56
  

Mesma chamada (area()), implementações diferentes.

Exemplo Resolvido – Polimorfismo com Interfaces

Uma classe pode implementar mais de uma interface. Dependendo de qual interface é usada, o objeto assume comportamentos diferentes.


// Interface de envio
interface Notificador {
    void enviar(String msg);
}

// Interface de registro
interface Registravel {
    void registrarLog(String msg);
}
    

// Implementações
class Email implements Notificador {
    public void enviar(String msg) {
        System.out.println("E-mail: " + msg);
    }
}

class PushNotification implements Notificador, Registravel {
    public void enviar(String msg) {
        System.out.println("Push: " + msg);
    }
    public void registrarLog(String msg) {
        System.out.println("Log Push: " + msg);
    }
}

   

class SMS implements Notificador, Registravel {
    public void enviar(String msg) {
        System.out.println("SMS: " + msg);
    }
    public void registrarLog(String msg) {
        System.out.println("Log SMS: " + msg);
    }
}
    

// Uso polimórfico

public class Sistema {
    public static void main(String[] args) {
        List n = List.of(
            new Email(),
            new PushNotification(),
            new SMS()
        );
        for (Notificador x : n) {
            x.enviar("Promoção!");
        }

        List r = List.of(
            new PushNotification(),
            new SMS()
        );
        for (Registravel x : r) {
            x.registrarLog("OK");
        }

    }
}
    

Explicação: O mesmo objeto pode ser usado como Notificador ou Registravel. O sistema não sabe qual classe concreta está sendo usada — ele apenas chama os métodos das interfaces.

Resumindo...

Polimorfismo é a capacidade de chamar o mesmo método e obter resultados diferentes, dependendo do objeto que o executa.

Acontece principalmente por:

  • Sobrescrita de métodos
  • Interfaces e classes abstratas

Exercício – Múltiplas Interfaces em Java

Tema: Sistema de Dispositivos Inteligentes (Smart Home)

Você foi contratado para modelar parte de um sistema de automação residencial em Java. Nesse sistema, diferentes dispositivos podem possuir capacidades distintas, como ligar/desligar, se conectar à internet e enviar relatórios de status.

Implemente o sistema seguindo os requisitos a seguir:

  1. Crie as interfaces abaixo:
    1. Ligavel
      • Método void ligar();
      • Método void desligar();
      • Método boolean isLigado();
    2. Conectavel
      • Método void conectar(String rede);
      • Método void desconectar();
      • Método boolean isConectado();
    3. Monitoravel
      • Método String gerarRelatorioStatus();
  • Crie a classe abstrata Dispositivo com:
    • Atributos protegidos: String nome; e String local;
    • Construtor que receba nome e local.
    • Método String getIdentificacao() que retorne algo no formato:
      "nome (local)".
  • Implemente as classes concretas:
    1. LampadaInteligente
      • Deve estender Dispositivo e implementar Ligavel e Monitoravel.
      • Armazene internamente o estado ligado/desligado.
      • No relatório (gerarRelatorioStatus), retorne algo como:
        "Lâmpada X (Sala): ligada" ou "Lâmpada X (Sala): desligada".
    2. CameraSeguranca
      • Deve estender Dispositivo e implementar Ligavel, Conectavel e Monitoravel.
      • Armazene internamente:
        • se está ligada ou não;
        • se está conectada, e o nome da rede.
      • No relatório, inclua se está ligada e se está conectada (e em qual rede).

Classe Principal – AppCasaInteligente


// Programa principal

import java.util.ArrayList;
import java.util.List;

public class AppCasaInteligente {

    public static void main(String[] args) {

        // Lista de dispositivos que podem ser monitorados
        List dispositivos = new ArrayList<>();

        // Criação dos dispositivos
        LampadaInteligente lampadaSala = new LampadaInteligente("Lâmpada Sala", "Sala");
        CameraSeguranca cameraGaragem = new CameraSeguranca("Câmera Garagem", "Garagem");

        dispositivos.add(lampadaSala);
        dispositivos.add(cameraGaragem);

        System.out.println("=== Ligando dispositivos ligáveis ===");
        for (Monitoravel m : dispositivos) {
            if (m instanceof Ligavel) {
                Ligavel l = (Ligavel) m;
                l.ligar();
            }
        }

        System.out.println("\n=== Conectando dispositivos conectáveis ===");
        for (Monitoravel m : dispositivos) {
            if (m instanceof Conectavel) {
                Conectavel c = (Conectavel) m;
                c.conectar("WiFi-Casa");
            }
        }

        System.out.println("\n=== Relatórios de Status ===");
        for (Monitoravel m : dispositivos) {
            System.out.println(m.gerarRelatorioStatus());
        }
    }
}
  

Exemplo de Saída do Programa


=== Ligando dispositivos ligáveis ===
Lâmpada Sala (Sala) foi ligada.
Câmera Câmera Garagem (Garagem) foi ligada.

=== Conectando dispositivos conectáveis ===
Câmera Câmera Garagem (Garagem) conectada à rede WiFi-Casa.

=== Relatórios de Status ===
Lâmpada Lâmpada Sala (Sala): ligada
Câmera Câmera Garagem (Garagem): ligada, conectada à rede WiFi-Casa