Anything could go wrong. Yes, it did.

That’s why we use “defensive programming,” or paranoid habits. Here, in my opinion, are 10 of the most useful but paranoid Java programming techniques. Take a look:

1. Place the String first

To prevent accidental NullPointerexceptions, String comparisons are usually performed by placing strings to the left of equals() :

// Badif (variable.equals("literal")) { ... }// Goodif ("literal".equals(variable)) { ... }
Copy the code

This is something you can just do in your head, going from the Bad version of code rewriting expressions to the Good version of code without losing anything in between. Different points of view are welcome…

2. Don’t trust the early JDK apis

In the early days of Java, programming was a pain. Those apis are pretty rudimentary, and you may have encountered the following code block:

String[] files = file.list(); // Watch outif (files ! = null) { for (int i = 0; i < files.length; i++) { ... }}Copy the code

Paranoid? Maybe, but look at Javadoc:

If the virtual path does not represent a folder directory, this method returns NULL. Otherwise, an array of strings is returned, each string representing a file or folder in the directory.

Yes, that’s right. We can add some checksums:

if (file.isDirectory()) {    String[] files = file.list();    // Watch out    if (files != null) {        for (int i = 0; i < files.length; i++) {            ...        }    }}
Copy the code

3. Don’t trust -1

I know this is paranoid, but Javadoc’s string.indexof () method makes it clear that the first occurrence of a given character’s positional index in an object, if -1, indicates that the character is not in the character sequence. So it makes sense to use negative 1, right? I said wrong, please look at the following code:

// Badif (string.indexOf(character) ! = 1) {... }// Goodif (string.indexOf(character) >= 0) { ... }Copy the code

Who knows. Maybe they’ll change the way they encode the string so it’s case insensitive, maybe it’s better to return -2? Who knows.

4. Avoid accidental assignments

Yes, that probably happens a lot.

// Ooopsif (variable = 5) { ... }// Better (because causes an error)if (5 = variable) { ... }// Intent (remember. Paranoid JavaScript: ===)if (5 === variable) { ... }
Copy the code

So you can place the comparison constant to the left, so that there is no accidental assignment error.

5. Check Null and Length

Anyway, as long as you have a collection, array, etc., make sure it exists and is not empty.

// Badif (array.length > 0) { ... }// Goodif (array ! = null && array.length > 0) { ... }Copy the code

You don’t know where these arrays came from, maybe from an earlier version of the JDK API, who knows.

6. All methods are final

You might tell me your on/off rule, but that’s bullshit. I don’t trust you (correctly inheriting all of my subclasses), and I don’t trust myself (accidentally inheriting all of my subclasses). So strictly use final for methods that make sense.

// Badpublic void boom() { ... }// Good. Don't touch.public final void dontTouch() { ... }
Copy the code

All variables and parameters are final

Like I said. I don’t trust myself (don’t accidentally overwrite my values).

// Badvoid input(String importantMessage) { String answer = "..." ; answer = importantMessage = "LOL accident"; }// Goodfinal void input(final String importantMessage) { final String answer = "..." ; }Copy the code

8. Don’t trust generics when reloading

Yes, it can happen. You trust that you’ve written a super good-looking API, it’s intuitive, and along the way, some user who just converted the primitive type to Object, until the damn compiler stops whining, and suddenly they’ll link to the wrong method, thinking it’s your fault.

Look at the following code:

// Bad<T> void bad(T value) { bad(Collections.singletonList(value)); }<T> void bad(List<T> values) { ... }// Goodfinal <T> void good(final T value) { if (value instanceof List) good((List<? >) value); else good(Collections.singletonList(value)); }final <T> void good(final List<T> values) { ... }Copy the code

Because, you know… Your users, they’re like:

// This library sucks@SuppressWarnings("all")Object t = (Object) (List) Arrays.asList("abc"); bad(t);Copy the code

Believe me. I’ve seen it all.

9. Always throw an exception in Default of the Switch statement

A Switch statement… I don’t know if I should be in awe of or cry over one of these ridiculous statements, but anyway, since we’re sticking with switch, we might as well make it perfect. Here’s the code:

// Badswitch (value) { case 1: foo(); break; case 2: bar(); break; }// Goodswitch (value) { case 1: foo(); break; case 2: bar(); break; default: throw new ThreadDeath("That'll teach them"); }Copy the code

When value == 3, there will be a hint that it cannot be found, rather than being confused.

10.Switch statement with curly braces

In fact, switch is the most evil statement, like some drunken or gambled loser writing code, as shown in the following example:

// Bad, doesn't compileswitch (value) { case 1: int j = 1; break; case 2: int j = 2; break; }// Goodswitch (value) { case 1: { final int j = 1; break; } case 2: { final int j = 2; break; } // Remember: default: throw new ThreadDeath("That'll teach them"); }Copy the code

In switch statements, each case statement has only one line in scope. In fact, these case statements are not even real statements; they are like jump markers in goto statements.

conclusion

Paranoid programming may seem incredible, sometimes, because the code often turns out to be a little more detailed, but not as the requirements require. You might think, “Oh, that’s never going to happen,” but as I said. After years of programming, you don’t want to just fix stupid bugs because the programming language is so archaic and flawed. Because you know…

Now it’s your turn! What are your most obsessive programming quirks?