In my opinion there is a difference between returning NULL, returning some empty result (e.g. the empty string or an empty list), and throwing an exception.
I normally take the following approach. I consider a function or method f(v1, ..., vn) call as the application of a function
f : S x T1 x ... x Tn -> T
where S it the "state of the world" T1, ..., Tn are the types of input parameters, and T is the return type.
I first try to define this function. If the function is partial (i.e. there are some input values for which it is not defined) I return NULL to signal this. This is because I want the computation to terminate normally and tell me that the function I have requested is not defined on the given inputs. Using, e.g., an empty string as return value is ambiguous because it could be that the function is defined on the inputs and the empty string is the correct result.
I think the extra check for a NULL pointer in the calling code is necessary because you are applying a partial function and it is the task of the called method to tell you if the function if not defined for the given input.
I prefer to use exceptions for errors that do not allow to carry out the computation (i.e. it was not possible to find any answer).
For example, suppose I have a class Customer and I want to implement a method
Customer findCustomer(String customerCode)
to search for a customer in the application database by its code.
In this method, I would
- Return an object of class Customer if the query is successful,
- Return null if the query does not find any customer.
- Throw an exception if it is not possible to connect to the database.
The extra checks for null, e.g.
Customer customer = findCustomer("...");
if (customer != null && customer.getOrders() > 0)
are part of the semantics of what I am doing and I would not just "skip them" in order to make the code read better. I do not think it is a good practice to simplify the semantics of the problem at hand just to simplify the code.
Of course, since the check for null occurs very often is it good if the language supports some special syntax for it.
I would also consider using the Null Object pattern (as suggested by Laf) as long as I can distinguish the null object of a class from all other objects.