A few weeks back I wrote about Maps and how MapUtils.isEmpty from Apache Commons Collections made the null-checks go away with isEmpty. Since then, I've been poking around the rest of MapUtils and it turns out the isEmpty method is not the only goodie hidden in there. MapUtils also has getter methods that are arguably even more useful, especially in a codebase where pretty much everything is a Map<String,Object>, for better or for worse (ok, probably for worse).
Here's the pattern I've been untangling lately:
// this is messy, since we always use Map<String,Object>
String myString = (String) myMap.get("someKey");
That cast is doing a lot of quiet work: it assumes the key exists, that the value is non-null, and that it's actually a String. Any one of those assumptions failing gives you a NullPointerException or a ClassCastException at runtime.
The first improvement I reached for is in the standard Map interface: getOrDefault:
// this is what I have been doing, to fix the NPEs
String myString = (String) myMap.getOrDefault("someKey", "");
This is somewhat better, I mean at least now missing keys don't blow up. But the cast is still there, and if some upstream code stuck an Integer in that slot, I still get a ClassCastException.
Enter MapUtils.getString:
// but this is better
String myString = MapUtils.getString(myMap, "someKey");
No casting at all! It handles the type coercion for me, and if the key is missing or the map is null, it returns null instead of throwing an exception. And of course, you can supply a default:
// but this is even better, because it saves the null
String myString = MapUtils.getString(myMap, "someKey", "");
Now I've got a one-liner that handles a null map, a missing key, a null value, and a value of the wrong type, without a cast and without a chain of if statements.
And it's not just getString. There's a whole family: getInteger, getLong, getBoolean, getDouble, getMap, and so on, and each has a default-value overload. If you live in Map<String,Object> land, these will save you a lot of defensive code.
What strikes me about all of this is that Commons Collections has been sitting in our classpath for years. It's not a new dependency, it's not exotic, it's just there. I've been writing casts and null checks by hand when the library I was already using had a cleaner answer the whole time. A reminder that it's worth occasionally re-reading the Javadocs or poking through the methods in autocomplete to see what you are missing (that is exactly what I did here).




