C++ A language with statically typed references to objects illustrates how the null object becomes a more complicated pattern: import std; using std::unique_ptr; class Animal { public: virtual ~Animal() = default; virtual void makeSound() const = 0; }; class Dog: public Animal { public: virtual void makeSound() const override { std::println("Woof!"); } }; class NullAnimal: public Animal { public: virtual void makeSound() const override { // silence... } }; int main() { unique_ptr dog = std::make_unique(); dog->makeSound(); // outputs "Woof!" unique_ptr unknown = std::make_unique(); unknown->makeSound(); // outputs nothing, but does not throw a runtime exception } Here, the idea is that there are situations where a pointer or reference to an Animal object is required, but there is no appropriate object available. A null reference is impossible in standard-conforming C++. A null Animal* pointer is possible, and could be useful as a place-holder, but may not be used for direct dispatch: a->makeSound() is undefined behavior if a is a null pointer. The null object pattern solves this problem by providing a special NullAnimal class which can be instantiated bound to an Animal pointer or reference. The special null class must be created for each class hierarchy that is to have a null object, since a NullAnimal is of no use when what is needed is a null object with regard to some Widget base class that is not related to the Animal hierarchy. Note that NOT having a null class at all is an important feature, in contrast to languages where "anything is a reference" (e.g., Java and C#). In C++, the design of a function or method may explicitly state whether null is allowed or not. // Function which requires an Animal instance, and will not accept null. void doSomething(const Animal& animal) { // animal may never be null here. } // Function which may accept an Animal instance or null. void doSomething(const Animal* animal) { // animal may be null. }
C# C# is a language in which the null object pattern can be properly implemented. This example shows animal objects that display sounds and a NullAnimal instance used in place of the C# null keyword. The null object provides consistent behaviour and prevents a runtime null reference exception that would occur if the C# null keyword were used instead. namespace Wikipedia.Examples; // Null object pattern implementation: using System; // Animal interface is the key to compatibility for Animal implementations below. interface IAnimal { void MakeSound(); } // Animal is the base case. abstract class Animal : IAnimal { // A shared instance that can be used for comparisons public static readonly IAnimal Null = new NullAnimal(); // The Null Case: this NullAnimal class should be used in place of C# null keyword. private class NullAnimal : Animal { public override void MakeSound() { // Purposefully provides no behaviour. } } public abstract void MakeSound(); } // Dog is a real animal. class Dog : Animal { public override void MakeSound() { Console.WriteLine("Woof!"); } } /* ========================= * Simplistic usage example in a Main entry point. */ static class Program { static void Main() { IAnimal dog = new Dog(); dog.MakeSound(); // outputs "Woof!" /* Instead of using C# null, use the Animal.Null instance. * This example is simplistic but conveys the idea that if the Animal.Null instance is used then the program * will never experience a .NET System.NullReferenceException at runtime, unlike if C# null were used. */ IAnimal unknown = Animal.Null; //
Smalltalk Following the Smalltalk principle,
everything is an object, the absence of an object is itself modeled by an object, called nil. In the GNU Smalltalk for example, the class of nil is UndefinedObject, a direct descendant of Object. Any operation that fails to return a sensible object for its purpose may return nil instead, thus avoiding the special case of returning "no object" unsupported by Smalltalk designers. This method has the advantage of simplicity (no need for a special case) over the classical "null" or "no object" or "null reference" approach. Especially useful messages to be used with nil are isNil, ifNil: or ifNotNil:, which make it practical and safe to deal with possible references to nil in Smalltalk programs.
Common Lisp In Lisp, functions can gracefully accept the special object nil, which reduces the amount of special case testing in application code. For instance, although nil is an atom and does not have any fields, the functions car and cdr accept nil and just return it, which is very useful and results in shorter code. Since nil
is the empty list in Lisp, the situation described in the introduction above doesn't exist. Code which returns nil is returning what is in fact the empty list (and not anything resembling a null reference to a list type), so the caller does not need to test the value to see whether or not it has a list. The null object pattern is also supported in multiple value processing. If the program attempts to extract a value from an expression which returns no values, the behavior is that the null object nil is substituted. Thus (list (values)) returns (nil) (a one-element list containing nil). The (values) expression returns no values at all, but since the function call to list needs to reduce its argument expression to a value, the null object is automatically substituted.
CLOS In Common Lisp, the object nil is the one and only instance of the special class null. What this means is that a method can be specialized to the null class, thereby implementing the null design pattern. Which is to say, it is essentially built into the object system: ;; empty dog class (defclass dog () ()) ;; a dog object makes a sound by barking: woof! is printed on standard output ;; when (make-sound x) is called, if x is an instance of the dog class. (defmethod make-sound ((obj dog)) (format t "woof!~%")) ;; allow (make-sound nil) to work via specialization to null class. ;; innocuous empty body: nil makes no sound. (defmethod make-sound ((obj null))) The class null is a subclass of the symbol class, because nil is a symbol. Since nil also represents the empty list, null is a subclass of the list class, too. Methods parameters specialized to symbol or list will thus take a nil argument. Of course, a null specialization can still be defined which is a more specific match for nil.
Scheme Unlike Common Lisp, and many dialects of Lisp, the Scheme dialect does not have a nil value which works this way; the functions car and cdr may not be applied to an empty list; Scheme application code therefore has to use the empty? or pair? predicate functions to sidestep this situation, even in situations where very similar Lisp would not need to distinguish the empty and non-empty cases thanks to the behavior of nil.
Ruby In
duck-typed languages like
Ruby, language inheritance is not necessary to provide expected behavior. class Dog def sound "bark" end end class NilAnimal def sound(*); end end def get_animal(animal=NilAnimal.new) animal end get_animal(Dog.new).sound => "bark" get_animal.sound => nil Attempts to directly
monkey-patch NilClass instead of providing explicit implementations give more unexpected side effects than benefits.
JavaScript In
duck-typed languages like
JavaScript, language inheritance is not necessary to provide expected behavior. class Dog { sound() { return 'bark'; } } class NullAnimal { sound() { return null; } } function getAnimal(type) { return type === 'dog' ? new Dog() : new NullAnimal(); } ['dog', null].map((animal) => getAnimal(animal).sound()); // Returns ["bark", null]
Java package org.wikipedia.examples; interface Animal { void makeSound(); } class Dog implements Animal { public void makeSound() { System.out.println("Woof!"); } } class NullAnimal implements Animal { public void makeSound() { // silence... } } public class Example { public static void main(String[] args) { Animal dog = new Dog(); dog.makeSound(); // outputs "Woof!" Animal unknown = new NullAnimal(); unknown.makeSound(); // outputs nothing, but does not throw a runtime exception } } This code illustrates a variation of the C++ example, above, using the Java language. As with C++, a null class can be instantiated in situations where a reference to an Animal object is required, but there is no appropriate object available. A null Animal object is possible (Animal myAnimal = null;) and could be useful as a place-holder, but may not be used for calling a method. In this example, myAnimal.makeSound(); will throw a NullPointerException. Therefore, additional code may be necessary to test for null objects. The null object pattern solves this problem by providing a special NullAnimal class which can be instantiated as an object of type Animal. As with C++ and related languages, that special null class must be created for each class hierarchy that needs a null object, since a NullAnimal is of no use when what is needed is a null object that does not implement the Animal interface.
PHP interface Animal { public function makeSound(); } class Dog implements Animal { public function makeSound() { echo "Woof...\n"; } } class Cat implements Animal { public function makeSound() { echo "Meowww...\n"; } } class NullAnimal implements Animal { public function makeSound() { // silence... } } $animalType = 'elephant'; function makeAnimalFromAnimalType(string $animalType): Animal { switch ($animalType) { case 'dog': return new Dog(); case 'cat': return new Cat(); default: return new NullAnimal(); } } makeAnimalFromAnimalType($animalType)->makeSound(); // ..the null animal makes no sound function animalMakeSound(Animal $animal): void { $animal->makeSound(); } foreach ([ makeAnimalFromAnimalType('dog'), makeAnimalFromAnimalType('NullAnimal'), makeAnimalFromAnimalType('cat'), ] as $animal) { // That's also reduce null handling code animalMakeSound($animal); }
Visual Basic .NET The following null object pattern implementation demonstrates the concrete class providing its corresponding null object in a static field Empty. This approach is frequently used in the .NET Framework (String.Empty, EventArgs.Empty, Guid.Empty, etc.). Public Class Animal Public Shared ReadOnly Empty As Animal = New AnimalEmpty() Public Overridable Sub MakeSound() Console.WriteLine("Woof!") End Sub End Class Friend NotInheritable Class AnimalEmpty Inherits Animal Public Overrides Sub MakeSound() ' End Sub End Class ==Criticism==