Classes and Interfaces
Source: Effective Java, 3rd Edition — Chapter 4 by Joshua Bloch (Items 15–20)
Classes and interfaces are Java’s main abstractions. This lesson: hide internals, accessors, immutability, composition over inheritance, design-for-inheritance (or forbid it), and interfaces vs abstract classes.
Item 15: Minimize accessibility
Information hiding decouples components — parallel work, safer changes, reuse. Rule: lowest access that still works.
Top-level: public or package-private. Prefer package-private so you can delete it later. One-user types → private static nested (Item 24).
Members (increasing access): private → package-private (default) → protected → public. Start private; widen only when needed. protected on a public class is forever API + implementation commitment — rare.
You cannot narrow an override below the superclass (LSP). Interface methods in a class are public.
Testing: package-private for tests is OK; do not export API just for tests.
Public instance fields: almost never (Item 16). public static final constants OK if primitive or immutable refs. Never public static final Thing[] VALUES — arrays are mutable. Fix: unmodifiable List or defensive clone() in values().
Java 9 modules add export-based levels; mostly advisory outside the JDK — skip unless you need them.
Minimize accessibility
1 / 2Default choice for a brand-new top-level class's access level
Pick an answer to see if you are right.
Item 16: Accessors, not public fields
Public Point { public double x, y; } cannot change representation or enforce invariants. Public classes need private fields + getters/setters. Package-private / private nested classes may expose fields. Immutable public fields are still questionable. Do not copy AWT Point / Dimension.
Accessibility
1 / 2public static final Thing[] VALUES
Pick an answer to see if you are right.
Item 17: Minimize mutability
Immutable = no observable state change after construction.
- No mutators
- Not subclassable (
final, or private ctors + static factories) - All fields
final - All fields
private - Defensive copies of mutable components (never store/return client mutables)
Complex: plus / times return new instances (functional style; names as prepositions). Wins: simple state, thread-safe sharing, great map keys, failure atomicity, share internals (BigInteger.negate). Cost: many objects — mitigate with multistep primitives or a mutable companion (StringBuilder). Cache derived values in nonfinal fields only if the visible state never changes (String hash).
If mutable: shrink the state space (CountDownLatch). Prefer private final by default. Fully initialize in the constructor.
Item 18: Favor composition over inheritance
Implementation inheritance across packages breaks encapsulation — subclasses depend on undocumented self-use (InstrumentedHashSet: addAll calls add → double count). New superclass methods later can bypass overrides (security). Adding methods risks accidental override.
Composition + forwarding: wrap a Set, implement Set, forward; InstrumentedSet only overrides add / addAll. Works with any Set. Decorator / wrapper. SELF problem in callbacks. Prefer when “is-a” is false (Stack/Vector, Properties/Hashtable).
Composition over inheritance
1 / 2InstrumentedHashSet extends HashSet, overriding add and addAll to each increment a counter then call super
Pick an answer to see if you are right.
Item 19: Design for inheritance or prohibit it
Document self-use of overridable methods (@implSpec). Export few protected hooks; test by writing ~3 subclasses. Constructors / clone / readObject must not call overridable methods (Super/Sub prints null). Cloneable/Serializable burden subclasses.
Ordinary concrete classes: make final or private ctors + factories unless you truly design for extension. Wrappers beat subclassing for instrumentation.
Inheritance vs composition
1 / 2InstrumentedHashSet overrides add and addAll counting size then super.addAll
Pick an answer to see if you are right.
Item 20: Prefer interfaces to abstract classes
Interfaces: multiple implementation, retrofit existing classes, mixins (Comparable), nonhierarchical types (SingerSongwriter), wrappers. Default methods help; cannot default equals/hashCode/toString; limited fields.
Skeletal AbstractList / AbstractMapEntry: primitives abstract; rest implemented. Optional to extend — or simulate multiple inheritance with a private skeletal helper. Simple implementations (SimpleEntry) are concrete minimal classes.
Items 15–20 map
| Item | Move |
|---|---|
| 15 | Least access; no public mutable arrays |
| 16 | Public types → accessors |
| 17 | Immutable by default |
| 18 | Compose/wrap; don’t extend foreign concretes |
| 19 | Document for inheritance or seal it off |
| 20 | Interfaces (+ skeletal) over abstract type roots |
Next: Interfaces and Nested Classes (Items 21–25).