-
Website
http://andypalmer.com -
Original page
http://andypalmer.com/2008/08/returning-null-considered-dishonest/ -
Subscribe
All Comments -
Community
-
Top Commenters
-
Andy Palmer
2 comments · 1 points
-
Rob Myers
1 comment · 1 points
-
tswicegood
1 comment · 1 points
-
-
Popular Threads
I agree with avoiding nulls in a codebase if possible, but I hesitate at saying that we should define a new exception type for each possible reason a contract is broken. It's more effort and maintenance to handle all those exception when the error condition probably should occur unless by a programmer mistake in the first place.
It's an old debate to be sure, but what's the strengths and weaknesses of:
if (weHaveEnoughMoney()) {
giveMeADrink();
} else {
blinkNotEnoughMoneyLight();
}
versus:
try {
giveMeADrink()
} catch(NotEnoughMoneyException e) {
blinkNotEnoughMoneyLight();
}
So my main rule is: if you want to return null, you need to documen that and you need to use "return null;" to make a reviewer obvious in which cases you expect this return value.
http://itblog.eckenfels.net/archives/352-Null-returns-in-Java.html
Besides that I try to avoid null returns (for example with empty collections).
First, I've found that once you start returning null, you end up returning null a lot of places. You end up with lots of null tests.
Second, throwing the exception as soon as the problem is detected yields better debugging information. A null pointer exception may not be triggered until some time later in a much different part of the code.
Instead of throwing exceptions, though, you could use a simple hierarchy with a Null Object. So instead of returning a Drink, return a MachineResponse, an interface which is implemented by classes Drink and NotEnoughMoney. I've found an approach like this usually pays for itself in the long run in terms of expressiveness and extensibility without costing too much.
Would the exception type always be checked or would a runtime exception ever be a better solution?
When would it become the case that it's better to return a Null Object pattern variation of the object rather than throwing an exception?
In this case maybe throwing the exception is good but clearly you don't want to become too dependent on handling the system's state through the use of exceptions.
I was with you all the way until your preferred solution was to throw an exception.
IMHO Programming by exception is never a good idea. Exceptions should be used only for really weird debugging situations and not to return business logic.
Is there a chance that giveMeADrink() will not return a drink? Then instead of the method throwing a tantrum and yelling at the caller that there was not enough money, it could return a response object, which details the reasons why It can't.
As @jeremy points out, a better way would be to return something like a Maybe object, which has two implementations - when it can return a drink, it returns a Something which contains the drink. If there was a problem, it returns a Nothing with perhaps a message detailing why.
Maybe[Drink] giveMeADrink(){
if (weDontHaveEnoughMoney) return {new Nothing(notEnoughMoneyMessage)}
if (weDontHaveEnoughStock) return {new Nothing(weDontHaveEnoughStockMessage)}
return new Something(new Drink())
}
Maybe[Drink] drink = giveMeADrink()
if (drink.IsPresent) { ... do what you will with the drink.Value (in the something case, it will be the drink object) .. }
else { .. do whatever you want with drink.Value (in the nothing case it will be the message) ... }
If it's in the client code, then "Tell, Don't Ask" says that we should not base what we ask of the called object on the state of that object. That's a decision for that object to make. Let's just ask it and see what we get.
@eckes: I'm thinking that we might move to a DrinksChute object, which is a collection, but that's not the way it is at the moment
@jeremy: Null Object is a possibility, but it depends on the planned usage of the returned object. In this case, the Drink has no behaviour, so it doesn't make sense to have a NullDrink. If we can't return a real Drink, I want to know why
@mark: These exceptions are unchecked :-) I'll post some thoughts on that soon.
Null Object is useful where we are expecting some behaviour from the returned object rather than just existence.
A quick example that I thought of would be a NullTransaction on a BankAccount. This would have no effect on the balance of the account, but may be used to make a note in the transaction history.
@sarah: I see where you're coming from, and there are many possible workarounds, but I have asked the VendingMachine for a Drink. I don't want my clients to have to worry about DrinkResponses that may or may not have a drink, which then has to be extracted. The DrinkChute collection would be acceptable, but it's not the simplest thing in this example.
@all: This is a contrived example, but the exceptions aren't really being used for business logic. They are unchecked, so there is no reason to catch them unless you want to handle the situation. The system fails fast with a clear reason as to why and where the problem occured.
Obviously, this is all just my opinion :-)
I'll defend my choice of using Exceptions with this:
The client code needs a Drink to proceed, it makes no sense to continue running if we do not receive a drink. If we use any of the other solutions, we still have to check to see if it's ok to proceed (did we get our drink?). With the Exception route, the application stops and tells you why, without additional burden on the client.
The blinkNotEnoughMoneyLight() would live in the vending machine. I use it to bring up the point of how the code is intended to react to being able to give out a drink - blinking a light to the user indicating more money is needed.
There is no contract that there should always be a drink from a vending machine. There is probably a business rule that says that once the needed money has been given. The value of null would only given out when that precondition is violated, which would only happen from programmer error.
IMO classic programmers are usually implementing C-Style control flows! Programming languages with exception support however add more convenience and support to the case, BUT do not replace null-reference handling. Here we go (I know this isn't the perfect example):
if(( machine = getActiveMachine()) == null) { ... }
else if(( drink = getMeADrink() == null) { ... } ...
In the above example we don't expect something "exceptional" and everything is checked sequentially branching should a particular condition triggers - this is supposed to be the normal flow.
BUT exceptions are beautiful if it's really considered an "exception" and should pick an alternate flow instead of the regular normal flow. For instance, designer of openFile( ) method MIGHT decide to throw an exception if invalid filename supplied!
Good design employs a mixture of both types; the point is to properly decide what should be considered the "normal" flow and what should be the "exception" or "error" flow! The rest is THE ART OF DESIGN... in summary I stand for proper use of null-handling and exception-flows, not eliminating any of them...
I want my code to fail fast. If I don't get a Drink, I want my code to tell me why.
I don't want my client code, that is expecting to receive a Drink, to have to check that I didn't cheat him and give him a null instead :-)
Can you give me an example of when a null is a valid response to something that is expecting a Drink object? That is, can you describe a situation where an object that needs a Drink to work with, would be happy with a null?
Andy said: "I want my code to fail fast. If I don't get a Drink, I want my code to tell me why." As said before, both approaches can co-exist, and now you described a situation in which you shouldn't be using null-refs! Perhaps solutions such as response-types (normal flow with additional info), or exceptions (exception or error flow optionally with yet more info).
Examples for returning nulls (sure we have alternatives):
1- getDefaultDrinkOrNull( ) if no-default-drink is considered a typical response and we prefer handling it in normal flow, fortunately designer has cleared all potential confusion by proper naming.
2- in a linked-list I may like getNext( ) return next object or null for the trail gifting more simplicity for iterations.
I think the "drink" example is a bit too generic! We could better be discussing null issues by real-world scenarios. For example:
A- If the Drink type is actually a Customer in a web scenario then returning response-types or exceptions are probably better approaches.
B- In a getTempFile( ) : File I might go for exceptions if it cannot return a temp file!
C- And for getActiveUser in a security service I might prefer returning null for indicating no-active-user message - however, I myself have designed security services in which active users were returned by response-types and some specific exceptions too. My mind wasn't changed, they were just proper solutions for their unique requirements.
All I try to say is, we should NOT replace null-handling, BUT we may discuss a particular case in which null-handling is not the best.
If you don't want an exception, you could use TryGet pattern:
Drink drink;
if(vendingMachine.TryGetDrink(out drink))
{
// we got a drink!
}