Null Object
Null Object replaces null with a do-nothing implementation of the expected interface so callers skip null checks and special cases. It simplifies code but can mask genuine missing-value bugs that should be errors.
Null Object is a behavioral pattern that supplies a substitute object with neutral, do-nothing behavior instead of using a null reference. Where code would otherwise check for null before acting, it can call methods on the null object safely, because that object implements the expected interface and simply does nothing or returns harmless defaults. This removes repetitive guard clauses and the risk of null reference errors.
How It Works
The pattern defines the same interface or abstract type that real collaborators implement. A null object is a concrete implementation whose methods do nothing, return empty collections, return zero, or otherwise produce a safe no-op result. Code that needs a collaborator is given either a real object or the null object, and it treats both identically. The null object is usually stateless and can be a shared singleton, since it holds no data.
The absence of a value is thus represented by an object rather than by null, so the calling code has no special branch for the missing case.
When to Use It
Use Null Object when an object requires a collaborator that may be absent and you want to avoid scattering null checks, when a sensible default of doing nothing exists, and when the missing case genuinely warrants neutral behavior rather than an error. Common examples include a no-op logger, an empty iterator, a guest user with no permissions, or a default discount of zero.
Trade-offs
A null object can hide real problems: if something should have been present, silently doing nothing may mask a bug that an explicit null check or exception would have surfaced. It is not a fit when absence is an error that must be handled. Maintaining a parallel no-op implementation adds a small amount of code, and a poorly chosen neutral behavior can produce subtly wrong results. Modern option or maybe types offer an alternative that makes absence explicit in the type system.
Related Patterns
Strategy and State share the structure of swapping interface implementations; Null Object is the variant whose behavior is intentionally empty. The broader Special Case pattern, of which Null Object is the most common instance, returns objects representing other exceptional conditions such as an unknown customer or an out-of-range value. Factory code often decides whether to hand back a real object or the null object. Option, Maybe, and Result types are the functional alternative; they make absence explicit in the type system and force the caller to acknowledge it, which avoids the risk that a null object silently swallows a genuine error.
Example
A service accepts an optional Logger. Instead of guarding every call with if (logger != null), callers pass a NullLogger whose methods do nothing when logging is disabled. The service code always calls logger.info(...) unconditionally, and behavior is correct whether real logging or the null logger is supplied. The same idea yields a GuestUser with no permissions so authorization code never special-cases anonymous access, an empty NullIterator so collection traversal needs no emptiness check, and a NoOpMetrics collector for environments where telemetry is turned off. In each case the null object is a stateless singleton, since it carries no data, and it is chosen deliberately only where doing nothing is genuinely the correct behavior rather than a way to hide a missing dependency.