Skip to main content

Method Design

Source: Effective Java, 3rd Edition — Chapter 8 by Joshua Bloch (Items 49–52)

This lesson covers method (and constructor) design: validate parameters, defensive copies, careful signatures, and safe overloading.

Item 49: Check parameters for validity​

Detect bad inputs early — at the method entry — with clear exceptions. Silent wrong answers or corrupted object state are worse than a fast failure (failure atomicity, Item 76).

  • Objects.requireNonNull(x, "msg") — null checks (Java 7+); returns x for inline assignment.
  • Objects.checkIndex / checkFromToIndex — array/list bounds (Java 9+).
  • Document with @throws in Javadoc for public/protected APIs.
  • Private helpers may use assert (enabled with -ea) when callers are under your control.

Check parameters stored for later use even if unused immediately (factory storing an array reference). Copy before or after validation depending on TOCTOU — for mutable params, copy first, then validate copies (Item 50).

Skip explicit checks when validation is implicit in the computation (Collections.sort → ClassCastException), but beware wrong exception types (use exception translation, Item 73).

Item 50: Make defensive copies when needed​

Even in a “safe” language, mutable components (Date, arrays, collections) let clients break invariants unless you defensive-copy on input and often on output.

Broken Period with Date fields: client mutates end after construction. Fix:

  1. Constructor: new Date(start.getTime()) — copy before validating copies (TOCTOU).
  2. Accessors: return new copies, not internal references.
  3. Prefer new Date(...) over clone() on nonfinal types (untrusted subclasses).

Prefer immutable components (Instant, LocalDateTime) when possible (Item 17). Arrays are always mutable — clone before return (Item 15). Document when callers may keep aliases (transfer-of-ownership APIs).

Quiz

Defensive copies

1 / 2

Period(Date start, Date end) validates then assigns this.start = start without copying

Pick an answer to see if you are right.

Item 51: Design method signatures carefully​

Practical API hygiene:

  • Names: clear, conventional (Item 68); match Java library style.
  • Convenience methods: only when they pull their weight — avoid API bloat.
  • Parameter lists: aim for ≤4 parameters; split methods, helper holder classes, or builder-style invokers for long lists.
  • Parameter types: prefer interfaces over concrete classes (Map not HashMap, Item 64).
  • Booleans: prefer two-element enums when meaning isn’t obvious (TemperatureScale.CELSIUS vs true).

Item 52: Use overloading judiciously​

Overloading is resolved at compile time from static parameter types. Overriding is resolved at runtime from the object’s type.

CollectionClassifier overloads classify(Set), classify(List), classify(Collection) — loop variable is Collection<?>, so all three calls hit classify(Collection) → "Unknown Collection".

Fix: one method with instanceof (or double dispatch):

return c instanceof Set ? "Set" :
c instanceof List ? "List" : "Unknown Collection";

Autoboxing trap: list.remove(i) with int i calls remove(int index), not remove(Integer) — Set.remove autoboxes to remove(E). Cast to Integer when you mean element removal.

Guidelines: avoid multiple overloads with the same arity when confusing; don’t overload on different functional interfaces in the same position; constructor overloads need radically different parameter types when shared arity.

Quiz

Overloading vs overriding

1 / 2

for (Collection<?> c : arrays) classify(c) with classify(Set), classify(List), classify(Collection)

Pick an answer to see if you are right.

Items 49–52 map​

ItemMove
49Validate early; requireNonNull; document @throws
50Defensive-copy mutable params and returns
51Short lists; interface parameter types; enums over booleans
52Overloading ≠ overriding; beware autoboxing overload traps

Next: Varargs, Returns, and Documentation (Items 53–56).