Thursday, 12 March 2015

Java: handling null in getter method chains

Null is just something you need to deal with in Java. This post looks at the mechanism for handling chained method calls without causing a NullPointerException.

This post uses Java 8.

Beans

Here's a deep object graph for illustration purposes:

public class Root {
    private Trunk trunk;

    public Trunk getTrunk() { return trunk; }
    public void setTrunk(Trunk trunk) { this.trunk = trunk; }

    public static class Trunk {
        private Branch branch;

        public Branch getBranch() { return branch; }
        public void setBranch(Branch branch) { this.branch = branch; }
    }

    public static class Branch {
        private Leaf leaf;

        public Leaf getLeaf() { return leaf; }
        public void setLeaf(Leaf leaf) { this.leaf = leaf; }
    }

    public static class Leaf {
        private String name;

        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
    }
}

Given a Root reference variable there are a lot of null checks between it and Leaf.name:

    public static void printLeafName(Root root) {
        String name = null;
        if(root != null) {
            Root.Trunk trunk = root.getTrunk();
            if (trunk != null) {
                Root.Branch branch = trunk.getBranch();
                if (branch != null) {
                    Root.Leaf leaf = branch.getLeaf();
                    if (leaf != null) {
                        name = leaf.getName();
                    }
                }
            }
        }
        System.out.println(name);
    }

Some languages provide facilities for handling this, like Groovy's safe navigation operator:

def void printLeafName(Root root) {
    def name = root?.trunk?.branch?.leaf?.name
    System.out.println(name)
}

Java lacks such an operator.

Ternary operator

Java's conditional operator often provides a succinct alternative to if statements but not in this case:

    public static void printLeafName(Root root) {
        String name = ((root == null)
                || (root.getTrunk() == null)
                || (root.getTrunk().getBranch() == null)
                || (root.getTrunk().getBranch().getLeaf() == null))
                ? null
                : root.getTrunk().getBranch().getLeaf().getName();
        System.out.println(name);
    }

It also comes at the expense of more getter calls, cheap though they are.

Optional

Combining the Java 8 Optional type with method references gives reasonably clear code:

    public static void printLeafName(Root root) {
        String name = Optional.ofNullable(root)
                .map(Root::getTrunk)
                .map(Root.Trunk::getBranch)
                .map(Root.Branch::getLeaf)
                .map(Root.Leaf::getName)
                .orElse(null);
        System.out.println(name);
    }

The downside here is object allocation.

KλudJe

The KλudJe library takes a tangential approach to using method references to achieve the same thing with the Nullifier type:

    public static void printLeafName(Root root) {
        String name = Nullifier.eval(root, Root::getTrunk, Root.Trunk::getBranch, Root.Branch::getLeaf, Root.Leaf::getName);
        System.out.println(name);
    }

The advantage of this example over Optional is that there are no object allocations because Oracle JDK8 will reuse static method references.

No comments:

Post a Comment

All comments are moderated