Mar 9, 2015

Handling Nulls

Functions, operators and comparators often crash on null operands, e.g. when applying map to a List containing nulls. There are at least two competing approaches to handling nulls: Optional<T> and WeakLogic.
Optional<T>, introduced with Java 8, is a wrapper: variables of type T which might be null are wrapped by an Optional<T>. This works and is clean but highly invasive because types get affected throughout the system. Equal-methods work nicely without Optional due to their different strategy: on null they return false because no non-null object ever equals null. Functions accepting null are called weak. Weak functions are less invasive than Optional because types remain unchanged.
Our class Weaklogic contains some high order functions transforming functions, operators and comparators into their weak counterpart. The idea is this: ignore null operands as long as there is at least one non-null operand, otherwise return null.
So weakEquals compares any two objects using their equals-method if possible and returning true on two nulls. Likewise weakUnaryOperator(UnaryOperator) returns a UnaryOperator identical to the argument but for its gently accepting null.
Weak comparators consider null the smallest element of the universe. weakComparator comes in two flavours: the one without argument returns a weak version of Comparable::compareTo, the other one transforms a Comparator into its weak counterpart.
So weak operators are an appealing alternative if Optional is unsuitable for whatever reason. 

Integer sum;
BinaryOperator<Integer> weakAdd = weakBinaryOperator((Integer a, Integer b) -> a + b);
sum = weakAdd.apply(null, null); // returns null
sum = weakAdd.apply(1, null); // returns 1
sum = weakAdd.apply(null, 2); // returns 2
sum = weakAdd.apply(1, 2); // returns 3
Comparator<Integer> cmp = (x, y) -> x - y;
Comparator<Integer> w = weakComparator(cmp);
w.compare(0, 1); // result < 0 w.compare(null, 1); // result < 0
w.compare(0, null); // result > 0
w.compare(null, null); // result == 0
Integer s;
Function<Integer, Integer> weak2 = weakFunction(x -> 2 * x);
s = weak2.apply(5); // returns 10
s = weak2.apply(null); // returns null

No comments:

Post a Comment