This to me sounds like a reasonably common problem that junior to intermediate developers tend to face at some point: they either don't know or don't trust the contracts they are participating in and defensively overcheck for nulls. Additionally, when writing their own code, they tend to rely on returning nulls to indicate something thus requiring the caller to check for nulls.
To put this another way, there are two instances where null checking comes up:
Where null is a valid response in terms of the contract; and
Where it isn't a valid response.
(2) is easy. Either use asserts or let it fail. Asserts are a highly underused Java feature that was added in 1.4. The syntax is:
assert *<condition>*
or
assert *<condition>* : *<object>*
where <object>
's toString() output will be included in the error.
Assert throws an Error (AssertionError) is the condition is not true. By default, Java ignores asserts. You can enable the feature by passing the option -ea to the JVM. Its also more sophisticated than that where you can enable and disable asserts for individual classes and packages. This means that you can validate code with the asserts while developing and testing and disable them in a production environment although my testing has shown next to no performance impact from asserts.
Not using asserts in this case is OK because the code will just fail, which is what will happen if you use asserts. The only difference is that with asserts it might happen sooner, in a more meaningful way and possibly with extra information for you to figure out why it happened if you weren't expecting it.
(1) is a little harder. If you have no control over the code you're calling then you're stuck. If null is a valid response, you have to check for it.
If its code you do have control over however (and this is often the case) then its a different story. Avoid using nulls as a response. With methods that return collections, it's easy: return empty collections or arrays over nulls pretty much all the time.
With non-collections it might be harder. Consider this as an example: if you have this interfaces:
public interface Action {
void doSomething();
}
public interface Parser {
Action findAction(String userInput);
}
where Parser takes raw user input and finds something to do, perhaps if you're implementing a command line interface for something. Now you might make the contract that it returns null if theres no appropriate action. That leads the null checking you'er talking about.
An alternative solution is to never return null and instead do something like this:
public class MyParser implements Parser {
private static Action DO_NOTHING = new Action() {
public void doSomething() { /* do nothing */ }
};
public Action findAction(String userInput) {
// ...
if ( /* we can't find any actions */ ) {
return DO_NOTHING;
}
}
}
Compare:
Parser parser = ParserFactory.getParser();
if (parser == null) {
// now what?
// this would be an example of where null isn't (or shouldn't be) a valid response
}
Action action = parser.getAction(someInput);
if (action == null) {
// do nothing
} else {
action.doSomething();
}
to
ParserFactory.getParser().findAction(someInput).doSomething();
which is a much better design because it leads to more concise code.