What is a Class Invariant?
Learn what a class invariant is in OOP, how encapsulation enforces it, and see a Java BankAccount example with interview Q&A.
Expected Interview Answer
A class invariant is a condition about an object’s internal state that must hold true before and after every public method call, guaranteeing the object is never left in an inconsistent state that a caller could observe.
Invariants are established by the constructor and preserved by every public method: each method may temporarily break the invariant internally, but it must restore it before returning control to the caller. For example, a BankAccount class might enforce the invariant that balance is never negative — every deposit and withdrawal method validates its inputs so this stays true no matter which sequence of calls a client makes. Invariants are strongest when fields are private, because encapsulation is what prevents external code from mutating state in ways that bypass the validating methods and break the guarantee. Documenting invariants explicitly (in comments, assertions, or design-by-contract annotations) helps both implementers and reviewers reason about correctness without tracing every call path.
- Guarantees the object is always in a valid, usable state
- Lets callers rely on assumptions without re-checking them
- Makes reasoning about correctness local to the class
- Surfaces bugs early via assertions during development
AI Mentor Explanation
A cricket ground’s pitch must always be exactly 22 yards between the two sets of stumps, no matter what happens during preparation, rolling, or repair between overs. Groundstaff can temporarily disturb the surface mid-repair, but before play resumes the measurement is re-verified and restored. That is a class invariant: a condition — here, the 22-yard distance — that may be briefly violated during an internal operation but must always hold true whenever the object is visible to the outside world (the players).
Step-by-Step Explanation
Step 1
Define the rule
State the condition that must always hold about the object's fields, e.g. balance >= 0.
Step 2
Establish it in the constructor
Validate inputs so the invariant holds the moment the object is created.
Step 3
Preserve it in every public method
Each mutator validates its changes so the invariant is restored before returning.
Step 4
Rely on private state
Keep fields private so no external code can bypass the validating methods and break the guarantee.
What Interviewer Expects
- A precise definition distinguishing before/after method calls from mid-method state
- A concrete invariant example tied to a real class
- Recognition that encapsulation (private fields) is what makes invariants enforceable
- Awareness that invariants should be checked in constructors and every mutator
Common Mistakes
- Claiming the invariant must hold at every single line of code, including mid-method
- Confusing an invariant with a one-off precondition on a single method
- Forgetting the constructor must also establish the invariant, not just mutators
- Assuming invariants are automatic rather than something the developer must enforce
Best Answer (HR Friendly)
“A class invariant is basically a promise the class makes about itself — some condition, like an account balance never going negative, that always holds true whenever anyone outside the class looks at an object. Methods are allowed to work through messy intermediate steps internally, but by the time they finish, that promise has to be true again.”
Code Example
public class BankAccount {
private double balance; // invariant: balance >= 0 always
public BankAccount(double initial) {
if (initial < 0) throw new IllegalArgumentException("Negative opening balance");
this.balance = initial; // invariant established here
}
public void withdraw(double amount) {
if (amount <= 0 || amount > balance) {
throw new IllegalArgumentException("Invalid withdrawal");
}
balance -= amount; // invariant preserved before returning
}
public void deposit(double amount) {
if (amount <= 0) throw new IllegalArgumentException("Invalid deposit");
balance += amount; // invariant preserved before returning
}
public double getBalance() { return balance; }
}Follow-up Questions
- How do class invariants differ from method preconditions and postconditions?
- Why does encapsulation matter for enforcing invariants?
- How would you check an invariant using assertions in Java?
- Can a class invariant ever be temporarily broken, and when is that acceptable?
MCQ Practice
1. A class invariant must hold true at which points?
Invariants must hold at the observable boundaries of an object — before and after public method calls — not necessarily mid-method.
2. Which feature makes class invariants enforceable against outside interference?
Private fields prevent external code from mutating state directly, so only the validating methods can change it.
3. A typical invariant for a Stack class with a maximum capacity would be?
The invariant bounds size between zero and the maximum capacity at every observable point.
Flash Cards
Class invariant in one line? — A condition about an object's state that holds true before and after every public method call.
Where is an invariant first established? — In the constructor.
What makes invariants enforceable? — Private fields — encapsulation prevents external code from bypassing validation.
Can internal steps temporarily break the invariant? — Yes, as long as it is restored before the method returns.