The safe navigation operator is almost certainly my favorite operator in Groovy. It allows you to guard against NullPointerException(s) much more cleanly than defining a nasty if/else mess. Consider the following example.
class Book {
String title
Author author
}
class Author {
String firstName
String lastName
}
def author = new Author(firstName:'Jason')
def book = new Book(title:'Say no to NPEs', author:author)
println book.author?.lastName
Did you see that ?. operator on the last line of the code sample? That's the safe navigation operator, and it's the equivalent of writing the following if statement (although it's much more readable).
if (book.author != null) {
println book.author.lastName
}
The safe navigation operator essentially checks to see if the object you're about to invoke a method on is null. If it is null, it simply skips the method invocation and returns null. If it isn't null, it proceeds as usual. It works for method chaining too, so if you're worried about book being null, you could write
println book?.author?.lastName
This way, if either book or author are null, you simply print out null instead of causing a NullPointerException.
Since learning about the safe navigation operator, I've used it successfully in dozens of classes without any problem. Until last week. Consider the following line of code .
println book?.author?.firstName?.trim().concat(" is great.")
Thanks to the safe navigation operator we can write this concatenation of firstName and a sentence fragment without worrying about NullPointerException(s) and ugly if blocks. Or so I thought.
Looking at this line of code, I thought for sure I was safe from any sneaky NullPointerException. If book, author or firstName are null I'll simply end up printing null and not have to worry about the concat() method. After all, if the trim() method succeeds, there's no sense in guarding it's result for null. And that's where I was wrong.
I naively assumed the method chain would stop once the safe navigation operator encountered a null when in fact it does not. While the trim() method is safe from being called on a null firstName object, it will return null, upon which the concat() method is called. Oops!
3 Comments
Leave a comment
0 TrackBacks
Listed below are links to blogs that reference this entry: Groovy's Safe Navigation Operator Not as Safe as I Thought.
TrackBack URL for this entry: http://www.nearinfinity.com/mt/mt-tb.cgi/601



Don't you just want another safe nav operator between the trim and concat?
It doesn't seem to be a deficiency with the safe nav operator, but you just need to follow through on your intent to make the whole statement safe.
You're precisely correct David. I intended to convey the misconception I, and several of my coworkers, held in assuming the entire call chain would stop once a null was encountered. I apologize for not making that clear. The operator is working correctly. We were just assuming it worked differently.
thats perfectly normal expectable behaviour. get a clue.
it's not equivalent to
if (book.author != null) {
println book.author.lastName
}
it's equivalent to
println ( book.author != null ? book.author.lastName : null );
which should explain a lot. at least if you know how the ternary is evaluated...but i guess not...haha