In my current job we are rewriting some code to Java 8. If you have code like this:
if(getApi() != null && getApi().getUser() != null
&& getApi().getUser().getCurrentTask() != null)
{
getApi().getUser().getCurrentTask().pause();
}
you can simply rewrite it to
Optional.ofNullable(this.getApi())
.map(Api::getUser)
.map(User::getCurrentTask)
.ifPresent(Task::pause);
without changing code behaviour. but what if something in the middle can throw NPE because it is not checked to null?
for example:
if(getApi() != null && getApi().getUser() != null
&& getApi().hasTasks())
{
getApi().getMasterUser(getApi().getUser()) //<- npe can be here
.getCurrentTask().pause();
}
what is the best way to rewrite code like this using optionals?(it should work exactly the same and throw npe when getMasterUser(...)
returns null)
UPD second example:
if(getApi()!=null && getApi.getUser() != null)
{
if(getApi().getUser().getDepartment().getBoss() != null)// <- nre if department is null
{
getApi().getUser().getDepartment().getBoss().somefunc();
}
}
it has nullchecks for api, user, boss, but not department. how can it be made using optionals?
Optional is a container object used to contain not-null objects. Optional object is used to represent null with absent value. This class has various utility methods to facilitate code to handle values as 'available' or 'not available' instead of checking null values.
In Java 8, we can use . map(Object::toString) to convert an Optional<String> to a String .
The need for Optional In any Java application, it is a common pattern to use method chaining to de-reference object properties. This often leads to the NullPointerException whenever there is a null object reference.
By the way, here is how Optional is described in the Java SE 11 documentation: “ Optional is primarily intended for use as a method return type where there is a clear need to represent 'no result,' and where using null is likely to cause errors.
A code rewrite gives you the benefit of experience – you know the weaknesses of the old system, the design flaws, current requirements and future road map. You can plan for it, design a system that will overcome the challenges and plan for the future.
You incrementally replace parts of the old system, you can still use your E2E and integration tests because the functionality hasn’t changed and the calling code is still in the old system. Another benefit is that you can pause or slow down the rewrite if the business requires it while having the ability to choose any stack you wish.
You can create an Optional instance that can contain a value by using the of () method. This method provides us the flexibility to create an optional with null or with a non-null reference. Following is the code snippet to create an optional with the null reference.
The optional class has no public constructor and expected to be accessed through the provided static factory methods As discussed, an optional instance can only be created through the factory methods provided in the Optional class. Following are the three factory methods:- You can create an empty instance of Optional by using the empty () method.
if(getApi() != null && getApi().getUser() != null) {
if(getApi().getUser().getDepartment().getBoss() != null) {
getApi().getUser().getDepartment().getBoss().somefunc();
}
}
One way of writing this with optionals is:
Optional.ofNullable(this.getApi())
.map(Api::getUser)
.map(user -> Objects.requireNonNull(user.getDepartment()))
.map(Department::getBoss)
.ifPresent(Boss::somefunc);
But this is error-prone because it requires the client to keep track of what is and isn't optional. A better way would be to make the api itself return an optional instead of a nullable value. Then the client code is:
this.getApi()
.flatMap(Api::getUser)
.map(user -> user.getDepartment().getBoss())
.ifPresent(Boss::somefunc));
This would make it clearer in the api which values should be optional and make it a compile-time error to not handle them.
if(getApi() != null && getApi().getUser() != null && getApi().hasTasks()) {
getApi().getMasterUser(getApi().getUser()).getCurrentTask().pause();
}
Here, you need access to api
and user
at the same time so you probably need to nest the lambdas:
getApi().filter(Api::hasTasks).ifPresent(api -> {
api.getUser().ifPresent(user -> {
api.getMasterUser(user).getCurrentTask().ifPresent(Task::pause);
});
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With