Médio Java

Conta com invariante de saldo

Enunciado

Implemente a classe Conta com titular (String) e saldo (double) privados. O saldo nunca pode ficar negativo. Forneça depositar(double) e sacar(double) que lancem IllegalArgumentException para valores não positivos e IllegalStateException quando o saque exceder o saldo.

Requisitos

  • titular e saldo são private.
  • depositar rejeita valores <= 0 com IllegalArgumentException.
  • sacar rejeita valores <= 0 com IllegalArgumentException e valores maiores que o saldo com IllegalStateException.
  • O main demonstra um depósito, um saque e captura uma tentativa inválida.

Código inicial

public class Conta {
    private String titular;
    private double saldo;

    public Conta(String titular, double saldoInicial) {
        // TODO: valide e atribua
    }

    public String getTitular() { return titular; }
    public double getSaldo() { return saldo; }

    // TODO: depositar e sacar com validações

    public static void main(String[] args) {
        // TODO: demonstre depósito, saque e uma tentativa inválida
    }
}

Saída esperada

Saldo: 120.0
Tentativa inválida: saldo insuficiente
Ver dica

Use throw new IllegalStateException("saldo insuficiente"); quando valor > saldo.

Mostrar solução
public class Conta {
    private String titular;
    private double saldo;

    public Conta(String titular, double saldoInicial) {
        if (titular == null || titular.isBlank()) {
            throw new IllegalArgumentException("titular obrigatório");
        }
        if (saldoInicial < 0) {
            throw new IllegalArgumentException("saldo inicial inválido");
        }
        this.titular = titular;
        this.saldo = saldoInicial;
    }

    public String getTitular() { return titular; }
    public double getSaldo() { return saldo; }

    public void depositar(double valor) {
        if (valor <= 0) throw new IllegalArgumentException("valor deve ser positivo");
        saldo += valor;
    }

    public void sacar(double valor) {
        if (valor <= 0) throw new IllegalArgumentException("valor deve ser positivo");
        if (valor > saldo) throw new IllegalStateException("saldo insuficiente");
        saldo -= valor;
    }

    public static void main(String[] args) {
        Conta c = new Conta("Ana", 100.0);
        c.depositar(50.0);
        c.sacar(30.0);
        System.out.println("Saldo: " + c.getSaldo());

        try {
            c.sacar(1000.0);
        } catch (IllegalStateException e) {
            System.out.println("Tentativa inválida: " + e.getMessage());
        }
    }
}

Passo a passo

  1. titular e saldo são private; fora da classe ninguém altera o saldo diretamente.
  2. O construtor valida titular (não nulo/vazio) e saldoInicial (não negativo) antes de atribuir.
  3. depositar rejeita valores <= 0 com IllegalArgumentException, pois é um argumento inválido.
  4. sacar primeiro valida o argumento; depois verifica se valor > saldo e lança IllegalStateException, porque o problema é o estado atual da conta, não o argumento.
  5. No main, depositamos 50, sacamos 30 (saldo 120) e tentamos sacar 1000 dentro de um try/catch para mostrar a mensagem.

Por que funciona

O invariante "saldo nunca é negativo" é mantido porque toda alteração passa por depositar/sacar, que validam antes de mudar o campo. A distinção entre IllegalArgumentException (entrada ruim) e IllegalStateException (estado impede a operação) deixa o erro mais informativo.

Erros comuns

  • Fazer saldo -= valor; antes de checar: if (valor > saldo) throw ... precisa vir primeiro, senão o saldo pode ficar negativo.
  • Usar Exception genérica: dificulta o tratamento específico no catch.
  • Permitir depositar(0): dependendo da regra de negócio, zero não é um depósito válido; aqui rejeitamos com <= 0.

Outra forma de resolver

Você pode centralizar a validação de valores em um método privado:

private static void exigirPositivo(double v) {
    if (v <= 0) throw new IllegalArgumentException("valor deve ser positivo");
}

Saída esperada

Saldo: 120.0
Tentativa inválida: saldo insuficiente