Let's end this week with log output and some java basics and talk about
the following toString()
method:
@Override
public String toString() {
return "Car{" +
"id=" + id +
", name='" + name + '\'' +
", owner=" + owner.getId() +
'}';
}
Well, do you see a problem with it? Not so far, you might say. So, when will
this method be called? I'd say that, in most business applications out there,
you won't really need your toString()
implementation. Except, when debugging
your application.
For this case, you've added the following line to your code:
LOG.debug("Processing {}", car);
Still see no problem? Well, there is one, and I'd say it's a big issue. As soon
as you enable DEBUG
log for your application, your code might cause a
NullPointerException
. Namely when your car
does not have an owner
.
Let's assume our owner
is null
. This might never be a problem for the rest of
your business logic, and it might even be intended. But at some point you could run
into a (possibly completely unrelated) issue and want to debug that on your
production machine.
You enable DEBUG
output, and suddenly your logging framework (slf4j, in my
particular case) starts using your toString()
implementation. But recall what
we did there, especially think about the following line:
", owner=" + owner.getId() +
But as said, our car owner does not exist, it is null
. So, we run straight into
the mentioned NullPointerException
.
Would you've expected that, when you'd added the simple LOG.debug(...)
line
from above? Would you've even seen that it could be risky for your business logic
to add this debug statement in your code?
Be careful when calling members of member variables.
Activating log output should never, ever affect your business logic. So, what do
we learn from this? I'd suggest, in methods like toString()
always add your
member variables directly, without any reference to their members. For this
particular implementation, just remove .getId()
.
@Override
public String toString() {
return "Car{" +
"id=" + id +
", name='" + name + '\'' +
", owner=" + owner +
'}';
}
And if you really care about a short output and really want the toString()
method to only write out the ID of the owner, you must add a null-check. You
could use Optional.ofNullable(owner)...
or just this simple solution:
@Override
public String toString() {
return "Car{" +
"id=" + id +
", name='" + name + '\'' +
", owner=" + owner != null ? owner.getId() : "null" +
'}';
}
However, this will bring complexity to your toString()
method, and complexity
brings the risk for other mistakes, bugs, and problems.
So, keep your code simple and always be cautious when accessing members of member variables. So much for that — have a relaxing weekend without any downtime!