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