Encapsulation

Encapsulation keeps an object's state and the code that guards it in one place, and lets nothing else change that state directly. It is how a class keeps its promises.

Overview

Encapsulation puts data and the operations on that data in one class, and closes every other door to the data. A BankAccount keeps its balance private; the only ways to change it are deposit and withdraw, and those methods enforce the rules: no negative amounts, no overdraft beyond the limit, every change recorded. Because no other code can touch the balance, those rules hold everywhere, forever.

The practical value is in the numbers. An invariant (a rule about state that must always be true) is only as strong as the number of places that can break it. With a public field, that number is 'every file in the codebase'. With a private field and two methods, it is two. Encapsulation also buys freedom: the class can change how it stores the balance, from an integer to a ledger of entries, and no caller notices.

The state and its rules, in one place

  • Invariants that hold — the only code that writes the field also checks the rule
  • Freedom to change representation — callers see methods, not fields
  • A smaller surface to learn and test — the public methods are the whole story
  • Safe concurrency later — a lock inside the class covers every path to the state

Access levels

Languages enforce the boundary differently, and interviewers expect the Java table exactly.

  • public — yes — yes — yes — yes
  • protected — yes — yes — yes — no
  • no modifier — yes — yes — no — no
  • private — yes — no — no — no

Oracle's guidance is to use the most restrictive level that works, and to avoid public fields except for constants. Other languages spell the same intent with less or more enforcement.

  • Java, C++, C# — private — by the compiler
  • JavaScript — #field inside the class body — by the language; access from outside is a syntax error
  • Python — _name by convention; __name is rewritten to _ClassName__name — no; the docs say private variables that cannot be accessed from outside do not exist
  • Go — lower-case names stay inside the package — by the compiler, at package level

Getters, setters and properties

A getter and a setter for every field is not encapsulation; it is a public field with more typing. The question is what control the class needs over each read and write.

  • Public field — none — never for state with rules; constants only
  • Getter only — callers can read, not write — derived or read-only values
  • Method with a domain name (withdraw) — full: validation, side effects, logging — state with rules
  • Setter with validation — some: the rule runs, but callers still think in fields — simple bounded values
  • Python @property — a method that reads like a field; a setter can be added later — when callers already use attribute syntax
  • Immutable object — writes are impossible after construction — values compared by their fields

Python's property is the tool for evolving a class: a plain attribute can become a computed or validated one without changing a single caller.

The reference that leaks

The most common hole in an encapsulated class is a getter that returns the real inner collection. The field is private, but every caller now holds a handle to it and can change it without going through any method.

  • the list itself — yes — none; the invariant is gone
  • a copy (list(items), List.copyOf) — no — O(n) per call
  • an unmodifiable view (Collections.unmodifiableList) — no; modifying it throws — O(1); the view reads through to later changes
  • an immutable element type plus a copy — no, at any depth — the cleanest

Java's Collections.unmodifiableList returns a view: reads pass through to the underlying list, and any attempt to modify it throws UnsupportedOperationException.

What you will be able to do

  • Say what invariant a class protects and show the one or two methods that are allowed to change its state.
  • Give the Java access table from memory and the Python, JavaScript and Go equivalents.
  • Decide between a public field, a getter, a domain method, a property and immutability for each field.
  • Spot a getter that leaks a mutable reference and fix it.
  • Explain tell-don't-ask and the Law of Demeter as consequences of encapsulation.

Interview questions

  • What are the access modifiers, and when do you use each?
  • Are getters and setters better than public fields?
  • How do you stop a getter from leaking a mutable reference?
  • What is tell-don't-ask, and how does the Law of Demeter follow from encapsulation?
  • How does immutability relate to encapsulation?
  • How do Java, Python and JavaScript enforce encapsulation differently?

Sign up free to read the full topic

More Low-Level Design topics

Start free on Cariara · Coding problems · System design studio