Médio Java

Sistema de pagamentos com instanceof pattern

Enunciado

Crie uma hierarquia de pagamentos: Pagamento (abstrata) com método processar(). As subclasses CartaoCredito e Boleto devem implementar processar() imprimindo mensagens específicas. Além disso, CartaoCredito tem um método autorizar() e Boleto tem um método gerarCodigoBarras(). No main, crie uma lista de Pagamento com um cartão e um boleto. Percorra a lista, chame processar() e, usando instanceof com pattern, chame o método específico de cada tipo.

Requisitos

  • Pagamento é uma classe abstrata com método abstrato processar().
  • CartaoCredito.processar() imprime "Processando pagamento com cartão de crédito." e autorizar() imprime "Autorizando cartão...".
  • Boleto.processar() imprime "Processando pagamento com boleto." e gerarCodigoBarras() imprime "Gerando código de barras...".
  • No main, use instanceof com pattern para chamar os métodos específicos sem casting explícito.

Código inicial

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

abstract class Pagamento {
    abstract void processar();
}

// TODO: crie CartaoCredito e Boleto

public class Main {
    public static void main(String[] args) {
        List<Pagamento> pagamentos = new ArrayList<>();
        // TODO: adicione um CartaoCredito e um Boleto
        // TODO: percorra e processe, usando instanceof pattern
    }
}

Saída esperada

Processando pagamento com cartão de crédito.
Autorizando cartão...
Processando pagamento com boleto.
Gerando código de barras...
Ver dica

Use if (p instanceof CartaoCredito c) { c.autorizar(); } para testar e converter em uma única linha.

Mostrar solução
import java.util.List;
import java.util.ArrayList;

abstract class Pagamento {
    abstract void processar();
}

class CartaoCredito extends Pagamento {
    @Override
    void processar() {
        System.out.println("Processando pagamento com cartão de crédito.");
    }

    void autorizar() {
        System.out.println("Autorizando cartão...");
    }
}

class Boleto extends Pagamento {
    @Override
    void processar() {
        System.out.println("Processando pagamento com boleto.");
    }

    void gerarCodigoBarras() {
        System.out.println("Gerando código de barras...");
    }
}

public class Main {
    public static void main(String[] args) {
        List<Pagamento> pagamentos = new ArrayList<>();
        pagamentos.add(new CartaoCredito());
        pagamentos.add(new Boleto());

        for (Pagamento p : pagamentos) {
            p.processar(); // despacho dinâmico

            // instanceof com pattern: declara variável já convertida
            if (p instanceof CartaoCredito c) {
                c.autorizar();
            } else if (p instanceof Boleto b) {
                b.gerarCodigoBarras();
            }
        }
    }
}

Passo a passo

  1. Definimos Pagamento como classe abstrata com o método processar(). Isso força as subclasses a implementarem o comportamento.
  2. CartaoCredito estende Pagamento e implementa processar() com a mensagem específica. Também adiciona o método autorizar().
  3. Boleto faz o mesmo, com processar() e gerarCodigoBarras().
  4. No main, criamos uma lista de Pagamento e adicionamos instâncias de CartaoCredito e Boleto. Isso é polimorfismo: a lista aceita qualquer subtipo.
  5. Iteramos com for-each. A chamada p.processar() é resolvida dinamicamente: para o cartão, executa a versão de CartaoCredito; para o boleto, a de Boleto.
  6. Usamos p instanceof CartaoCredito c para verificar se o objeto é um cartão e, em caso afirmativo, a variável c já está tipada como CartaoCredito, permitindo chamar c.autorizar() sem cast explícito.
  7. O else if faz o mesmo para Boleto, chamando b.gerarCodigoBarras().
  8. A saída segue a ordem de inserção na lista.

Por que funciona

O polimorfismo permite que a lista de Pagamento armazene diferentes tipos. O método processar() é sobrescrito e chamado dinamicamente. O instanceof com pattern é um recurso moderno que simplifica a verificação de tipo e o casting, tornando o código mais limpo e seguro. Sem ele, precisaríamos de um cast explícito após a verificação.

Erros comuns

  • Esquecer o else if: se usar dois if separados, ambos podem ser verdadeiros? Não, mas se um objeto for de um tipo, o outro if será falso. No entanto, usar else if deixa a intenção clara e evita verificações desnecessárias.
  • Ordem dos instanceof: se CartaoCredito fosse subclasse de Boleto (não é o caso), a ordem importaria. Aqui, como são irmãos, não há problema.
  • Não usar pattern: if (p instanceof CartaoCredito) { ((CartaoCredito) p).autorizar(); } funciona, mas é mais verboso e propenso a erros.

Outra forma de resolver

Poderia-se usar o método getClass().getSimpleName() para decidir, mas isso não é polimórfico e quebra o princípio de aberto/fechado. A forma com instanceof pattern é preferível.

Saída esperada

Processando pagamento com cartão de crédito.
Autorizando cartão...
Processando pagamento com boleto.
Gerando código de barras...