State the formal Law of Demeter rule: which objects may a method M of an object O legitimately send messages to?
answer
- this, fields, parameters, created objects, globals
- Return values are NOT on the list
- Object form (LoD-F) vs class form
- Collection elements ~ your components
- Adaptive programming = declarative traversal
basics
~20 sMethod M of object O may call methods on: O itself, O's own fields/components, M's parameters, objects M creates inside itself, and globals in scope. It may not call methods on objects returned by those calls.
solid answer
~50 sThe object-level Law of Demeter (LoD-F, "for functions") says: inside method M of object O, you may send messages only to 1. **O itself** (`this`, including inherited methods), 2. **O's direct component objects** — its own fields, and the elements of collections it holds, 3. **M's parameters**, 4. **objects M instantiates itself**, 5. **objects in global/static scope accessible to O** (the original formulation includes this; modern practice discourages relying on it). Everything else is a *stranger*. Crucially, **the return value of a permitted call is not automatically permitted** — that's exactly what turns a legal call into a train wreck. There is also a **class-level** form, phrased over the *class* dependency graph: the classes a method may name are the class itself, its field types, its parameter types, and types it instantiates. The class form is stricter and is what static analysers usually check.
code
pseudocode · 10 linesclass Invoice(private val repo: Repo) { // repo = field -> friend
fun send(mailer: Mailer, order: Order) { // params -> friends
val pdf = PdfWriter() // created -> friend
pdf.write(this.header()) // self -> friend
repo.save(this) // field -> OK
mailer.send(pdf) // param -> OK
order.customer().email() // STRANGER: return value of a friend
}
}go deeper
Getting three of the five categories (self, fields, parameters) plus 'not the return values' is already a solid answer.
List all five categories and state the return-value clause explicitly — that clause is the whole rule.
Add the object-form vs class-form distinction, and discuss the edge cases (collections, fluent APIs, factories, DTOs) where the letter and the intent diverge.
Explain why those categories: each is a dependency already declared in a visible signature, whereas a stranger dependency is invisible. Reference adaptive programming as the Demeter project's answer to hard-coded traversals.
## The precise formulation The Law of Demeter is usually quoted informally ("only talk to your friends"), but it has a precise statement, and interviewers who go past the slogan ask for it. ### Object form — Law of Demeter for Functions (LoD-F) For a method **M** of an object **O**, M may only invoke methods of objects in these categories: | # | Category | Example | |---|----------|---------| | 1 | **O itself** | `this.validate()` — includes inherited and private methods | | 2 | **O's direct components** (its own fields; elements of collections it owns) | `this.repository.save(x)`, `for (line in this.lines) line.total()` | | 3 | **M's parameters** | `fun render(p: Printer) { p.print(...) }` | | 4 | **Objects M creates** | `val f = Formatter(); f.format(x)` | | 5 | **Objects in global / static scope accessible to O** | `Logger.get().info(...)` — permitted by the original text, disliked today because global state is its own coupling problem | **The key clause, and the one people forget:** *the object returned by a permitted call is not itself permitted.* `this.order` is fine (category 2). `this.order.customer()` is fine (a message to a friend). `this.order.customer().address()` is **not** — `customer()`'s return value is a stranger. ### Class form (Law of Demeter for Classes) Stated over static types rather than runtime objects: the *classes named* inside method M of class C are restricted to C itself, C's superclasses, the declared types of C's fields, M's parameter types, and types M instantiates. This is checkable by static analysis, and it is stricter — it forbids even *mentioning* a stranger type. The object form allows some cases the class form rejects (e.g. a factory returning a type already used as a field type). ### Why the categories are exactly these Each category is something the method **already, unavoidably, depends on**: - Your own class — you can't decouple from yourself. - Your fields — you chose them; that dependency is declared. - Your parameters — the caller handed them to you; the dependency is in your signature, visible to everyone. - What you construct — you already know the concrete type. What's excluded is the one thing you can *avoid* depending on: **transitive structure**. Every category above is a dependency that is *declared somewhere visible*. A stranger dependency is invisible — it hides inside a method body and no signature reveals it. ### Common edge cases - **Collections.** `this.items.get(0).price()` — is `items.get(0)` a stranger? By the letter, yes; by intent, no: elements of a collection you own are treated as your components. Most practitioners allow it. Note this reveals LoD's blind spot around generic container types. - **Fluent/self-returning APIs.** `builder.a().b().build()` — each call returns *the same object*, so no new stranger is introduced. Not a violation. - **Method chaining over values.** `list.filter(...).map(...).first()` returns new values of a type you already depend on; the graph of some other object isn't being walked. - **Injected factories.** `this.factory.create().run()` technically violates it; in practice a factory's product is usually a type you already depend on, and forbidding it forces awkward wrappers. - **Data structures / DTOs / parsed JSON.** LoD governs *objects with behaviour*. A record whose whole purpose is to expose structure is not a stranger-hiding collaborator; navigating it is legitimate. ### Related formulations The Demeter project also produced **adaptive programming** — write traversal strategies ("from Order go to City") declaratively so that when the graph shape changes, only the strategy changes, not the code. That is LoD's ideal taken to its conclusion: never hard-code the path through a graph. ### How to state it in an interview Name the five categories, then immediately add the punchline: *"…and the return value of any of those calls is **not** in the list — that's what makes `a.getB().getC()` a violation."* Then mention the class form exists and is what tools check.
- Is `this.items.get(0).price()` a Law of Demeter violation when `items` is a field of the current class?By the strict letter, yes — `get(0)` returns an object you did not directly hold. By intent, no: elements of a collection you own are conventionally treated as your own components. It exposes that LoD is phrased for object references, not container types.
- How does the class-level form of the Law of Demeter differ from the object-level form?The class form restricts which *types* a method may name (own class, supertypes, field types, parameter types, instantiated types); the object form restricts which *runtime objects* receive messages. The class form is stricter and statically checkable, which is why analysers implement it.