As one emarks on learning Java, they quickly become familiar with the NullPointerException (NPE). Whether this is a bug or intentional will largely be dependent on your codebase. Should you expect to see NULL more often than you'd like or if some data just isn't available because that's what the business model demands, Java offers Optional as a way to handle this.
What is an Optional?
According to the JavaDocs:
A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.
In other words, you can declare that what you are working with may contain a null value and avoid discovering it through an NPE. In an e-prescription, the abstraction of classes can be nested quite deeply. This necessitates null checks just to get to a possible value to work with since not all data is always mandatory for valid e-prescription.
Consider two classes: MedicationOrder and Quantity
public class MedicationOrder {
private String medicationName;
private String patientInstructions;
private Quantity quantity;
private Integer daysSupply;
//constructor, setters, getters
}
public class Quantity {
private Float amount;
private String unit;
//constructor, setters, getters
}
These two classes can be typical of a simple medication order that houses some details of what medication, how much medication the pharmacy should dispense and how the patient should take the medication.
If one needs to get the Quantity from the MedicationOrder class, there's no guarantee the Quantity object isn't null, then same for the Amount field (Although a good constructor should enforce both for Quantity as they are meaningless on their own). Secondly, a prescriber may only indicate the days supply and that would be a valid prescription, leaving Quantity null.
You will see an NPE if Quantity is null when trying to get the Amount in the following call:
MedicationOrder order = new MedicationOrder("Amoxicillin 250mg/5ml suspension", "10.5 mL tid for 7 days");
Float quantityAmount = order.getQuantity().getAmount();
// expect to get NPE at this line if Quantity is null
We can do null checks to make sure we don't hit an NPE but that quickly gets messy and hard to read:
private void printQuantityAmountUsingNullChecks() {
if (order.getQuantity() != null) {
if (order.getQuantity().getAmount() != null) {
System.out.println(order.getQuantity().getAmount());
}
}
}
Imagine you had further nested classes such as Unit class containing both unit name and a codeable concept, even more null checks!
Instead we could wrap the MedicationOrder object as an Optional, providing a cleaner and safer way to get data using map().
private void printQuantityUsingOptional() {
Optional<Float> optionalQuantityAmount = Optional.of(order)
.map(MedicationOrder::getQuantity)
.map(Quantity::getAmount);
optionalQuantityAmount.ifPresent(amount -> {
System.out.println(amount);
});
}
Optional.map() vs Optional.ifPresent()
In the previous example we see how one can chain Optional.map() and call object methods to eventually arrive at the desired data without having to use null checks explicity. This is handled under the hood by Optional. Then we can do something with the data.
Optional.ifPresent() is a method that takes a consumer function. That is, if the Optional object has a value, do something with it. In the previous example, we can take the province string and print it in the console.
Conclusion
If one wants to get data where a null might be possible, chaining map() calls is one way to do it efficiently after wrapping the object as an Optional. Then if one wants to do something with a possible non-null value in an Optional object, ifPresent() will let you do so using a consumer function and can be written using lamba notion.
This approach can be especially helpful when working with prescription and medication order class abstractions as fields can be null depending on the patient context.