Definition

Violation Example
// BAD: Exposed internal state
public class BankAccount {
public double balance; // Anyone can set!
public List<Transaction> transactions; // Direct access!
public String accountNumber;
public void deposit(double amount) {
balance += amount;
transactions.add(new Transaction("deposit", amount));
}
}
// Problems:
BankAccount account = new BankAccount();
account.balance = 1000000; // Fraud!
account.balance = -500; // Invalid state!
account.transactions.clear(); // Evidence destroyed!
account.transactions = null; // NPE waiting to happen!
// Also can't change implementation:
// - What if we want to store balance as cents (long)?
// - What if transactions should be stored in DB?
// - All code using these fields would break!
Proper Encapsulation
// GOOD: Properly encapsulated
public class BankAccount {
private BigDecimal balance;
private final List<Transaction> transactions;
private final String accountNumber;
public BankAccount(String accountNumber) {
this.accountNumber = accountNumber;
this.balance = BigDecimal.ZERO;
this.transactions = new ArrayList<>();
}
public void deposit(Money amount) {
if (amount.isNegative()) {
throw new IllegalArgumentException("Deposit must be positive");
}
this.balance = this.balance.add(amount.getValue());
this.transactions.add(Transaction.deposit(amount));
}
public void withdraw(Money amount) {
if (amount.isGreaterThan(this.balance)) {
throw new InsufficientFundsException();
}
this.balance = this.balance.subtract(amount.getValue());
this.transactions.add(Transaction.withdrawal(amount));
}
public Money getBalance() {
return new Money(balance); // Return copy, not reference
}
public List<Transaction> getTransactionHistory() {
return Collections.unmodifiableList(transactions); // Read-only!
}
// No setter for balance - only through deposit/withdraw
// No setter for accountNumber - immutable after creation
}
// Now:
// ✓ Can't set invalid balance
// ✓ Can't modify transactions directly
// ✓ Can change internal representation freely
// ✓ All mutations go through validated methods
Defensive Copying
// Protect mutable objects from external modification
public class Event {
private final String name;
private final Date startDate;
private final List<String> attendees;
// BAD: Direct assignment
public Event(String name, Date startDate, List<String> attendees) {
this.name = name;
this.startDate = startDate; // Reference to external Date!
this.attendees = attendees; // Reference to external List!
}
// GOOD: Defensive copying
public Event(String name, Date startDate, List<String> attendees) {
this.name = name;
this.startDate = new Date(startDate.getTime()); // Copy!
this.attendees = new ArrayList<>(attendees); // Copy!
}
// BAD: Return internal reference
public Date getStartDate() {
return startDate; // Caller can modify!
}
// GOOD: Return copy
public Date getStartDate() {
return new Date(startDate.getTime()); // Return copy!
}
// BETTER: Use immutable types (Java 8+)
private final LocalDateTime startDateTime; // Immutable!
private final List<String> attendees;
public List<String> getAttendees() {
return List.copyOf(attendees); // Immutable copy
}
}
Access Modifiers

Tell, Don't Ask
// Information hiding encourages "Tell, Don't Ask"
// BAD: Asking for data, making decision outside
class OrderProcessor {
void processOrder(Order order) {
// Asking for internal state
if (order.getStatus() == Status.PENDING &&
order.getPaymentStatus() == PaymentStatus.PAID &&
order.getItems().size() > 0) {
// Then do something...
}
}
}
// GOOD: Tell the object what to do
class Order {
// Order knows its own rules
public boolean canBeProcessed() {
return status == Status.PENDING &&
paymentStatus == PaymentStatus.PAID &&
!items.isEmpty();
}
public void process() {
if (!canBeProcessed()) {
throw new IllegalStateException("Cannot process order");
}
// Process...
}
}
class OrderProcessor {
void processOrder(Order order) {
order.process(); // Tell, don't ask!
}
}
// Benefits:
// - Logic about Order is in Order class
// - Can change Order internals without affecting OrderProcessor
// - Single place for validation logic
Module-Level Encapsulation

Tips & Tricks
