Defensive java programming is a great way to prepare for the inevitable to go wrong -- but it can be just as paranoid as it is useful.
After coding for a while (eek, almost 20 years or so in my case, time flies when you’re having fun), one starts to embrace those habits. Because, you know…
This is why people embrace “defensive programming”, i.e. paranoid habits that sometimes make total sense, and sometimes are rather obscure and/or clever and perhaps a bit eerie when you think of the person who wrote it. Here’s my personal list of top 10 useful, yet paranoid Java programming techniques. Let’s go:
1. Put the String Literal First
It’s just never a bad idea to prevent the occasional
NullPointerExceptionby putting the
Stringliteral on the left side of an
equals()comparison as such:
This is a no-brainer. Nothing is lost from rephrasing the expression from the less good version to the better one. If only we had true Options though, right? Different discussion…
2. Don’t Trust the Early JDK APIs
In the early days of Java, programming must’ve been a big pain. The APIs were still very immature and you might’ve walked across a piece of code like this:
Looking paranoid? Perhaps, but read the Javadoc:
If this abstract pathname does not denote a directory, then this method returns null. Otherwise an array of strings is returned, one for each file or directory in the directory.
Yeah right. Better add another check, though, just to be sure:
Bummer! Violation of rule #5 and #6 of our 10 Subtle Best Practices when Coding Java list. So be prepared and add that
3. Don’t Trust That “-1″
This is paranoid, I know. The Javadoc of
String.indexOf()clearly states that…
the index of the first occurrence of the character in the character sequence represented by this object [is returned], or -1 if the character does not occur.
-1can be taken for granted, right? I say nay. Consider this:
Who knows. Perhaps they’ll need ANOTHER encoding at some point in time to say, the
otherStringwould have been contained if checked case-insensitively… Perhaps a good case for returning
-2? Who knows.
After all, we’ve had billions of discussions about the billion dollar mistake, which is
NULL. Why shouldn’t we start discussions about
-1, which is – in a way – an alternative
nullfor primitive type
4. Avoid the Accidental Assignment
Yep. It happens to the best (although, not to me. See #7).
Again. If you have a literal in your expression, put it to the left side. You can’t accidentally go wrong here, when you meant to add another
5. Check for Null AND Length
Whenever you have a collection, array, etc., make sure it’s present AND not empty.
You never know where those arrays come from. Perhaps from early JDK API?
6. All Methods Are Final
You can tell me all you want about your open/closed principles, that’s all bollocks. I don’t trust you (to correctly extend my classes) and I don’t trust myself (to not accidentally extend my classes). Which is why everything that is not explicitly intended for subtyping (i.e. only interfaces) is strictly
final. See also item #9 of our 10 Subtle Best Practices when Coding Java list.
Yes. It’s final. If that doesn’t work for you, patch it, or instrument it, or rewrite the byte code. Or send a feature request. I’m sure that your intent of overriding the above isn’t a good idea anyway.
7. All Variables and Parameters Are Final
As I said. I don’t trust myself (to not accidentally overwrite my values). Having said so, I don’t trust myself at all. Because…
… which is why all variables and parameters are made
OK, I admit. This one, I don’t apply very often, really, although I should. I wish Java got it right like Scala, where people just type
valall over the place, without even thinking about mutability – except when they need it explicitly (rarely!), via
8. Don’t Trust Generics When Overloading
Yes. It can happen. You believe you wrote that super nice API which totally rocks and is totally intuitive, and along comes some user who just raw-casts everything up to
Objectuntil the darn compiler stops bitching, and suddently they’ll link the wrong method, thinking it’s your fault (it always is).
Because, you know… Your users, they’re like
Trust me. I’ve seen everything. Including things like
It’s good to be paranoid.
9. Always Throw on Switch Default
Switch… One of those funny statements where I don’t know whether to petrify with awe or to just cry. Anyway, we’re stuck with
switch, so we may as well get it right when we have to. I.e.
Because that moment where
value == 3is introduced into the software, it’ll come for sure! And don’t say
enum, because it’ll happen to
10. Switch With Curly Braces
switchis the most wicked statement anyone has every allowed to get into a language while they were either drunk or lost a bet. Consider the following example:
switchstatement, there is only one scope defined among all the
casestatements. In fact, these
casestatements aren’t even really statements, they’re like labels and the
switchis a goto call. In fact, you could even compare
casestatements with the astonishing FORTRAN 77 ENTRY statement, a device whose mystery is only exceeded by its power.
This means that the variable
final int jis defined for all the different cases, regardless if we issue a
breakor not. Not very intuitive. Which is why it’s always a good idea to create a new, nested scope per
casestatement via a simple block. (but don’t forget the
breakwithin the block!)
Paranoid programming may seem weird at times, as code often turns out to be a bit more verbose than really needed. You might think, “oh this is never gonna happen”, but as I said. After 20 years or so programming, you just don’t want to fix those stupid little unnecessary bugs anymore that exist only because the language is so old and flawed. Because you know…