# Exceptions and Design by Contract — Object-Oriented Programming (OOP)

Source: https://www.skillbyai.com/en/oop/o-errors

> Design classes that fail clearly and document their contracts.

## Making failures part of the design

A well-designed class makes its **contract** clear: **preconditions** (what callers must ensure, such as a positive amount), **postconditions** (what the method guarantees afterwards) and **class invariants** (what is always true for every object, such as "balance ≥ 0"). This idea, **design by contract**, comes from Bertrand Meyer's Eiffel language. In Java and similar languages, check preconditions at the start of public methods and **fail fast** with clear exceptions: `IllegalArgumentException` for bad arguments, `IllegalStateException` when the object is in the wrong state for the call, `NullPointerException` (often via `Objects.requireNonNull`) for missing values. Create **custom exceptions** for domain failures callers may handle (`InsufficientFundsException`). Java distinguishes **checked** exceptions (must be declared or handled, for recoverable conditions) from **unchecked** ones (programming errors); many modern codebases prefer unchecked exceptions or result types. Never swallow exceptions silently, and preserve the original cause when wrapping one exception in another.

## Preconditions, invariants and a domain exception

Invalid calls fail immediately with a clear message.

```java
public class InsufficientFundsException extends RuntimeException {
    public InsufficientFundsException(long requested, long available) {
        super("requested " + requested + " but only " + available + " available");
    }
}

public class Wallet {
    private long balancePaise;                                  // invariant: >= 0
    private boolean frozen;

    public void pay(long paise) {
        if (paise <= 0) throw new IllegalArgumentException("amount must be positive");   // precondition
        if (frozen) throw new IllegalStateException("wallet is frozen");
        if (paise > balancePaise) throw new InsufficientFundsException(paise, balancePaise);
        balancePaise -= paise;
        assert balancePaise >= 0 : "invariant broken";            // postcondition / invariant
    }
}
```

## Fail at the boundary, not deep inside

A negative amount accepted by a constructor might cause a confusing error much later in a report. Validating at the public boundary points straight to the caller that broke the contract.

**Quiz:** Which exception best signals that a method was called while the object is in the wrong state?

- [ ] IllegalArgumentException
- [ ] ArithmeticException
- [x] IllegalStateException
- [ ] ClassNotFoundException

*Answer:* IllegalStateException. IllegalStateException indicates the call is not valid for the object's current state.
