BackendBench
Coffee

Unravel โ€บ Java concepts โ€บ Errors and logging

Errors and logging

Defensive checks: assert and preconditions

Fail early and loudly: check arguments at the start of a method (Objects.requireNonNull, if (x < 0) throw new IllegalArgumentException(...)).

Learn these first

At a glance

Mental model

A bouncer at the door of a method: bad input is stopped right there with a clear message, instead of wandering inside and causing a confusing failure ten calls later.

What happens, in order
  1. 1this.name = Objects.requireNonNull(name, "name must not be null"); throws a NullPointerException with your message at the point of the mistake
  2. 2Preconditions.checkArgument(age >= 0, "age %s", age) (Guava), Assert.isTrue(...) (Spring) and Validate.notNull(...) (Commons Lang) are helper versions of the same idea
  3. 3assert total >= 0 : "negative total"; is checked only when the JVM runs with -eaIt is off in production by default, so it must never perform side effects or validate user input.
  4. 4Use argument errors (IllegalArgumentException) for bad input from the caller, IllegalStateException for "not allowed now", and Bean Validation annotations at the web boundary
Remember
Watch out
Putting side effects in an assertUsing assert to validate user inputSkipping validation in constructors and then finding broken objects everywhereEmpty or missing error messages
Example (highlighted lines explain it)
import java.util.Objects;public class Account {  private final String owner;  private long balance;  public Account(String owner, long balance) {    this.owner = Objects.requireNonNull(owner, "owner must not be null");    if (balance < 0) throw new IllegalArgumentException("balance must be >= 0 but was " + balance);    this.balance = balance;  }  public void withdraw(long amount) {    if (amount > balance) throw new IllegalStateException("insufficient funds");    balance -= amount;    assert balance >= 0 : "negative balance";  }}
  1. line 6 uses requireNonNull, the core of Defensive checks: assert and preconditions
Read the full explanation (about 2 min)

Fail early and loudly: check arguments at the start of a method (Objects.requireNonNull, if (x < 0) throw new IllegalArgumentException(...)). assert is a developer-only check that is switched off by default.

A mental model

A bouncer at the door of a method: bad input is stopped right there with a clear message, instead of wandering inside and causing a confusing failure ten calls later.

How it works

this.name = Objects.requireNonNull(name, "name must not be null"); throws a NullPointerException with your message at the point of the mistake.

Preconditions.checkArgument(age >= 0, "age %s", age) (Guava), Assert.isTrue(...) (Spring) and Validate.notNull(...) (Commons Lang) are helper versions of the same idea.

assert total >= 0 : "negative total"; is checked only when the JVM runs with -ea. It is off in production by default, so it must never perform side effects or validate user input.

Use argument errors (IllegalArgumentException) for bad input from the caller, IllegalStateException for "not allowed now", and Bean Validation annotations at the web boundary.

Going deeper

Validate at the boundary (controller, constructor, public method) and trust inside. Repeating null checks in every private method adds noise; annotations like @NonNull plus a static analyser add safety without runtime cost.

Use assert for "cannot happen" invariants in your own code while developing and in tests (run tests with -ea), not for checks that must always hold.

A message that contains the bad value ("age must be >= 0 but was -3") saves an hour of debugging. Do not include secrets.

Under the hood

The compiler turns assert x : y into if (!$assertionsDisabled && !x) throw new AssertionError(y). The static flag is set once when the class is initialised from the JVM option, so the check costs almost nothing when disabled.

Common mistakes

  • Putting side effects in an assert.
  • Using assert to validate user input.
  • Skipping validation in constructors and then finding broken objects everywhere.
  • Empty or missing error messages.

How Unravel recognises it

When you paste code, Unravel looks for these signs of defensive checks: assert and preconditions. Spotting them yourself is a good habit when you read unfamiliar code:

Part of a learning path

Related concepts

Frequently asked questions

What is Defensive checks: assert and preconditions?

Fail early and loudly: check arguments at the start of a method (Objects.requireNonNull, if (x < 0) throw new IllegalArgumentException(...)). assert is a developer-only check that is switched off by default.

What are the most common mistakes with Defensive checks: assert and preconditions?

Putting side effects in an assert. Using assert to validate user input. Skipping validation in constructors and then finding broken objects everywhere. Empty or missing error messages.

How can I see Defensive checks: assert and preconditions in real code?

Paste your own Java into Unravel and it will point out where defensive checks: assert and preconditions appears, explain how it works at the detail level you choose, and draw the flow of the code around it.