Encapsulation β
Encapsulation is one of the core principles of object-oriented programming (OOP). It means:
Bundling data and behavior together, and controlling access to internal state so the object protects its own consistency.
Encapsulation has two key aspects:
- Information Hiding
- Maintaining Invariants
Information Hidingβ
Information hiding means:
- The internal representation (data structures, variables) of a class is not directly accessible from outside.
- External code interacts only through a well-defined public interface (methods).
The goal:
- Prevent misuse
- Reduce coupling
- Allow internal changes without breaking other code
Why it matters
If internal details are exposed:
- Other parts of the program may depend on them.
- Changing the implementation becomes risky.
- Bugs can be introduced by uncontrolled modification.
Without Encapsulationβ
class BankAccount {
public double balance;
}
Usage:
BankAccount acc = new BankAccount();
acc.balance = -1000000; // Nothing prevents this
Problem:
- Anyone can assign invalid values.
- No control over how balance changes.
- Invariant (balance should not be negative) can be violated.
With Encapsulation (Information Hiding)β
class BankAccount {
private double balance;
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
}
}
public double getBalance() {
return balance;
}
}
balanceis private.- Only methods can modify it.
- Rules are enforced inside the class.
Invariantsβ
An invariant is a condition that must always be true for an object after construction and between method calls.
Examples:
- A bank account balance β₯ 0
- A rectangle width > 0 and height > 0
- A list size β₯ 0
- A personβs age β₯ 0
Encapsulation ensures invariants are protected.
Example with Invariant Enforcementβ
Letβs define a Rectangle.
Invariant:
- width > 0
- height > 0
Bad Design (Invariant Not Protected)β
class Rectangle {
public int width;
public int height;
}
Usage:
Rectangle r = new Rectangle();
r.width = -5; // Violates invariant
r.height = 0; // Also invalid
The object is now in an invalid state.
Proper Encapsulation with Invariant Protectionβ
class Rectangle {
private int width;
private int height;
public Rectangle(int width, int height) {
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("Width and height must be positive.");
}
this.width = width;
this.height = height;
}
public void setWidth(int width) {
if (width <= 0) {
throw new IllegalArgumentException("Width must be positive.");
}
this.width = width;
}
public void setHeight(int height) {
if (height <= 0) {
throw new IllegalArgumentException("Height must be positive.");
}
this.height = height;
}
public int getArea() {
return width * height;
}
}
Now:
- Object can never exist with invalid dimensions.
- Invariant is guaranteed after constructor and after each method call.
- Internal state is protected.
Key Concept: Encapsulation = Protection of Invariantsβ
You can think of it like this:
| Concept | Meaning |
|---|---|
| Information hiding | Hide internal state and implementation |
| Public interface | Controlled access through methods |
| Invariant | Condition that must always hold true |
| Encapsulation | Mechanism that ensures invariants cannot be violated |
Deep Insightβ
Encapsulation is not just about private variables.
It is about:
Designing a class so that it controls its own correctness.
Good encapsulation:
- Minimizes exposed surface
- Protects invariants
- Makes illegal states unrepresentable
- Allows internal refactoring without breaking users
Advanced View (Language Independent)β
Encapsulation can also mean:
- Returning copies instead of references (defensive copying)
- Using immutable objects
- Restricting mutation
- Avoiding exposing internal collections
Dangerous:β
public List<String> getItems() {
return items; // exposes internal list
}
Better:
public List<String> getItems() {
return new ArrayList<>(items); // defensive copy
}
This prevents external code from modifying internal state.
Summaryβ
Encapsulation consists of:
- Information Hiding
- Hide internal data
- Expose only necessary operations
- Invariant Protection
- Define rules that must always hold
- Enforce them in constructors and methods
- Never allow object to enter invalid state
And the golden rule:
If outside code can break your objectβs correctness, encapsulation is incomplete.