Skip to main content

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.

Quiz

Minimize accessibility

1 / 2

Default 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.

Quiz

Accessibility

1 / 2

public static final Thing[] VALUES

Pick an answer to see if you are right.

Item 17: Minimize mutability​

Immutable = no observable state change after construction.

  1. No mutators
  2. Not subclassable (final, or private ctors + static factories)
  3. All fields final
  4. All fields private
  5. 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).

Quiz

Composition over inheritance

1 / 2

InstrumentedHashSet 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.

Quiz

Inheritance vs composition

1 / 2

InstrumentedHashSet 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​

ItemMove
15Least access; no public mutable arrays
16Public types → accessors
17Immutable by default
18Compose/wrap; don’t extend foreign concretes
19Document for inheritance or seal it off
20Interfaces (+ skeletal) over abstract type roots

Next: Interfaces and Nested Classes (Items 21–25).