The is an object-oriented programming pattern that allows one to avoid null (i.e. undefined) values by instead creating objects that do nothing.
For example, say a library defines an interface ICommand with an execute method.
interface ICommand {
void execute();
}
If one uses null to represent undefined commands, then one would always need to check that a value is non-null prior to use.
class CommandExecutor {
ICommand command = null;
void runCommand() {
if (command != null) {
command.execute();
}
}
}
Such checks are verbose and easy to forget. Instead, it is better to define a NullCommand class that implements ICommand and whose execute method does nothing.
class NullCommand implements ICommand {
void execute() {
// do nothing
}
}
Then, one can initialize undefined ICommand values to a new NullCommand, removing the need for null checks.
class CommandExecutor {
ICommand command = new NullCommand();
void runCommand() {
command.execute();
}
}
One limitation of the pattern is that it requires access to the application's class hierarchy. What does one do when one needs to create a null object for a class that cannot be subclassed or constructed directly?
I recently encountered this problem while working on a research project. I was writing some reflective code that passed around objects. The Method class is declared final and has a non-public constructor, meaning that I could not create a corresponding NullMethod class for undefined values. Nevertheless, for the reasons described above, I wanted to avoid using null. The solution for this particular problem involved creating reflective instances of a static dummy method that adheres to the signature expected by the application.
To see how this works, let us return to the example given at the beginning of the post. Say the application uses Method objects instead of ICommand objects. In this new example, the original CommandExecutor class would look something like the following:
class CommandExecutor {
Method method = null;
void runCommand(Object instance) throws Exception {
if (method != null) {
method.invoke(instance);
}
}
}
This example assumes that the Method objects take no arguments other than the instance in which to execute the method and that the return type does not matter. I will discuss how to handle more complex method signatures later, but for now, we know our null objects should adhere to this simple signature and do nothing when invoked.
The following is the updated code using null object pattern. UNDEFINED_METHOD_IMPL defines the body of the null method, and UNDEFINED_METHOD returns its reflective instance. This reflective instance is the null object that one can use to initialize undefined values such as the method field.
class CommandExecutor {
public static Method UNDEFINED_METHOD() {
try {
return CommandExecutor.class.getDeclaredMethod(
"UNDEFINED_METHOD_IMPL");
} catch // removed for space
}
public static void UNDEFINED_METHOD_IMPL() {
// do nothing
}
Method method = UNDEFINED_METHOD();
void runCommand(Object instance) throws Exception {
method.invoke(instance);
}
}
Because UNDEFINED_METHOD_IMPL is static, the invoke call ignores the instance variable. This means the method can be invoked on any type even though it is declared in CommandExecutor.
What if the expected method signature is more complex? For example, what if the application requires methods to accept one String argument and return an object of type ICommand?
class CommandExecutor {
...
void runCommand(Object instance, String arg) throws Exception {
ICommand command = (ICommand) method.invoke(instance, arg);
command.execute();
}
}
In this case, we simply change UNDEFINED_METHOD_IMPL such that it adheres to the new signature and returns a new NullCommand. Essentially, the null object (method) returns a null object (command).
class CommandExecutor {
public static Method UNDEFINED_METHOD() {
try {
return CommandExecutor.class.getDeclaredMethod(
"UNDEFINED_METHOD_IMPL",
String.class);
} catch // removed for space
}
public static ICommand UNDEFINED_METHOD_IMPL(String arg) {
return new NullCommand();
}
Method method = UNDEFINED_METHOD();
void runCommand(Object instance, String arg) throws Exception {
ICommand command = (ICommand) method.invoke(instance, arg);
command.execute();
}
}
Note that a reflective instance of a dummy method is not a true null value since its methods still execute normally and return "real" values. For example, still returns the for CommandExecutor. Therefore, this approach works well when invoking methods, but not when an application requires deep reflective exploration of Method objects.
This Java file contains the working example code.