If you already use Option, Maybe or Optional in your code then move along please – nothing to see here. Otherwise here’s a neat trick you have been missing out on…
The Null Object Refactoring / Pattern
Examine the code below. All seems fine, unless the call to ‘buildPaycheck’ can return null, in which case we need to insert a conditional to check the return value.
double calculateTotalPayroll() {
double totalSalary = 0;
for (Employee employee : buildListOfStaff()) {
Paycheck paycheck = employee.buildPaycheck();
//TODO: maybe insert 'if (paycheck != null)' here?
totalSalary += paycheck.basicWage();
if (paycheck.isEligibleForPerformanceBonus()) {
totalSalary += paycheck.bonus();
}
}
return totalSalary;
}
We can avoid this by applying Null Object. Specifically we define a class that extends from Paycheck
and define its behaviour so that it will pass through our code without causing any side-effects:
class NullPaycheck extends Paycheck {
@Override
public double basicWage() {
return 0;
}
@Override
public double bonus() {
return 0;
}
@Override
public boolean isEligibleForPerformanceBonus() {
return false;
}
}
This works well in some scenarios, but in practice we cant define a null type for every hierarchy that is guaranteed to have no effect on the calling code. For example a printPaychecks
method would still print a wage of 0.0 for certain Employees.
The solution is to return a collection, where that collection is a set of zero or one Paycheck objects. We refer to a set of size 1 as a Some<Paycheck>
and a set of size zero as a None<Paycheck>
. This is known as the Option Monad.
The Option Monad
To get a handle on Option examine the Scala classes below. We have a Person
class with a findAddress
method, an Address
class with a findPostcode
method and a Postcode
class with a findValue
method. Instead of these methods returning null they return an Option
of T.
As mentioned above Option
is best thought of as a set of size 0 or 1. The None
type represents an Option
of size 0, whereas a Some(T)
represents an Option
of size 1.
Here is a simple Scala example, with methods returning Option
:
class Person(val name: String, val address: Address) {
def findAddress(): Option[Address] = {
if (address == null) {
None
} else {
Some(address)
}
}
}
class Address(val number: Int,
val street: String,
val postcode: Postcode) {
def findPostcode(): Option[Postcode] = {
if (postcode == null) {
None
} else {
Some(postcode)
}
}
}
class Postcode(val value: String) {
def findValue(): Option[String] = {
if (value == null) {
None
} else {
Some(value)
}
}
}
Because the methods all use Option
we can chain the calls together using the syntax below, knowing that the chain will halt if any call returns None
. Note the same syntax can be used to combine other constructs that count as Monads.
def printPostcodeIfExists(person: Person) {
for (
place <- person findAddress;
code <- place findPostcode;
result <- code findValue
) println(result)
}
Simulating the Option Monad in C# via LINQ
In the .NET world F# developers have the same feature, but under the name of the Maybe Monad. It is not natively supported in C#, but you can define your own Option hierarchy and then integrate it with the LINQ syntax (as below). For more details see Mike Hadlow’s excellent series of blog posts.
public abstract class Option<T> {
public abstract bool Worked { get; }
public abstract T Value { get; }
}
public class None<T> : Option<T> {
public override bool Worked { get { return false; } }
public override T Value { get { return default(T); } }
}
public class Some<T> : Option<T> {
public override bool Worked { get { return true; } }
public override T Value { get { return value; } }
public Some(T value) {
this.value = value;
}
private T value;
}
To make the Option type work with LINQ does require some scary code:
public static class MonadOperations {
public static Option<T> ToOption<T>(this T value) {
return new Some<T>(value);
}
public static Option<U> Bind<T, U>(this Option<T> theOption,
Func<T, Option<U>> func) {
if (theOption is Some<T>) {
return func((theOption as Some<T>).Value);
}
else {
return new None<U>();
}
}
public static Option<V> SelectMany<T, U, V>(this Option<T> theOption,
Func<T, Option<U>> func,
Func<T, U, V> select) {
return theOption.Bind(first => func(first).Bind(second => select(first, second).ToOption()));
}
}
To chain C# method calls that return an option via LINQ we would do the following:
class Program {
static void Main(string[] args) {
var testPerson1 = new Person("Dave", new Address(new Postcode("ABC 123")));
var testPerson2 = new Person("Pete", new Address(new Postcode(null)));
var testPerson3 = new Person("Fred", new Address(null));
var testPerson4 = new Person("Lucy", null);
PrintPostcode(testPerson1);
PrintPostcode(testPerson2);
PrintPostcode(testPerson3);
PrintPostcode(testPerson4);
}
private static void PrintPostcode(Person person) {
var result = from r in person.Residence
from p in r.Location
from t in p.Text
select t;
if (result.Worked) {
Console.WriteLine("{0} lives at \"{1}\"", person.Name, result.Value);
} else {
Console.WriteLine("Could not retrieve postcode for {0}", person.Name);
}
}
}
The Optional Type in Java 8
Historically Java has not had an option-like concept, although libraries like Guava have tried to provide one. However Java 8 includes an Optional class similar to those discussed. Lets look at an example.
The fetchSystemProperty method below attempts to find the JVM property with the provided name. If the property exists we return an Optional of 1 and an empty Optional otherwise. The caller can:
- Execute arbitrary code by invoking isPresent with a lambda or method reference
- Supply a default value by invoking orElse
public class Program {
private static Optional<String> fetchSystemProperty(String name) {
String result = System.getProperty(name);
if (result == null) {
return Optional.empty();
} else {
return Optional.of(result);
}
}
public static void main(String [] args) {
Optional<String> result1 = fetchSystemProperty("java.version");
Optional<String> result2 = fetchSystemProperty("wibble");
result1.ifPresent(s -> System.out.println(s));
result2.ifPresent(s -> System.out.println(s));
result1.ifPresent(System.out::println);
result2.ifPresent(System.out::println);
System.out.println(result1.orElse("No such property!"));
System.out.println(result2.orElse("No such property!"));
}
}
It's great to now have a standardised Option type in the language, but without language support for Monads or an (official) LINQ-like library its not as useful. But that doesn’t mean you shouldn’t be using it. For example we can integrate it easily into the initial example:
double calculateTotalPayroll() {
double totalSalary = 0.0;
for (Employee employee : buildListOfStaff()) {
Optional<Paycheck> option = employee.buildPaycheck();
Paycheck paycheck = option.orElse(new NullPaycheck());
totalSalary += paycheck.basicWage();
if (paycheck.isEligibleForPerformanceBonus()) {
totalSalary += paycheck.bonus();
}
}
return totalSalary;
}
Of course what we would really like to be able to do is something like:
option.ifPresent(paycheck -> totalSalary += paycheck.basicWage())
But sadly this is not possible since, as with inner and anonymous inner classes, only final or ‘effectively final’ variables can be used in lambdas.
Conclusion
The Option(al) concept is more versatile than the Null Object Pattern / Refactoring and ‘surfaces’ the problem of null values in your code. Used correctly it can drastically reduce the number of NullPointerExceptions in your code, which has to be a good thing :-)