Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I inherited a project that I am managing and having to maintain pending the redevelopment of the code base. At the moment I am being tasked with adding little features all over the place and have gotten into the habit of returning null instead of zero in parts of the code where I am working on.

The problem is we have a client that is using this code and parts of code that require data from my implemented features receive a null and dump the stack trace in the UI.

I would like to avoid this entirely from my input but without the NullPointerException there's the potential that errors would be introduced into the client's data which may go un-noticed.

Usually I would have come up with my own error notification system but I have never inherited a project before. So I am unsure whether to continue down this path. I still believe that the stack dump is preferable to un-noticed data corruption/inaccuracies.

share|improve this question
1  
Which language? Each behaves differently, so advice for one might not be appropriate for another. –  Jan Hudec May 31 '12 at 9:45
2  
Yes, in a language with exceptions you can use those instead of returning null. –  Carra May 31 '12 at 9:46
1  
@Carra if the client code does not handle exception, it would probably just be the same –  Louis Rhys May 31 '12 at 9:56
5  
You are aware of the NullObject pattern? –  user1249 May 31 '12 at 10:27
    
Java. J2EE platform –  Dark Star1 Jun 5 '12 at 8:52

5 Answers 5

up vote 3 down vote accepted

I am currently reading Robert C. Martin's Clean Code and I finished the chapter on exception handling a few days ago. What he says is :

Returning null from methods is bad, but passing null into methods is worse

He goes on listing concrete examples on how you can avoid that, mainly:

  • when returning lists or arrays, return empty list or arrays
  • when returning non-collection objects, prefer returning "special case" objects, OR throwing exceptions. By "special case", I suppose it is for example returning an empty string (StringUtils.EMPTY if you work with the Apache Commons package), or a "NullObject" (thanks Thorbjørn I did not know it was a pattern in fact)...

If you build your application this way, you will have fewer "null checks" to write, something he mentions by saying that more often than not, having methods with a lot of "null checks" is a bad sign.

NullPointerException should be expected during development, when working with an API that may not be clear with how it treats its arguments, and I think they should be considered bugs in production code. I mean that if I work with an API I know and understand, I don't expect myself to catch NullPointerException, and I expect to see them in the log files if I do something wrong in fact (which sadly still happens after programming for a few years now :-).

Of course, as always with "best practices", you don't have to blindly follow these principles. These are "best practices", not "this-is-the-only-good-way practices" :-)

I did not read the Andrew Hunt and David Thomas' Pragmatic Programmer ... yet, but this blog link could be interesting. It explains in a few words the slight differences in exception handling between the two books, if you are curious.

share|improve this answer
    
Did he mention anything about the Option (Scala) or Maybe (Haskell) notions at all? They seem like the perfect answer to this problem. Here's a Java (your use is implied by referring to the NullPointerException) implementation of the Option notion: synhaptein.com/synhaptein/2012/02/14/scala-option-in-java.html –  chaotic3quilibrium Jun 4 '12 at 14:49
    
He does not mention Scala or Haskell from what I remember but he refers to the Special Case pattern from Martin Fowler (martinfowler.com/eaaCatalog/specialCase.html) which is a somewhat different way to deal with the problem than with the Option pattern (which works too of course) –  Jalayn Jun 4 '12 at 15:04

Its a matter of personal preference and how important having all correct data is.

Returning an obviously wrong but still valid number is less likely to cause unexpected exceptions in the client but runs the risk of that error going unnoticed. (often -1 is used rather than zero as its more clearly incorrect rather than a real result)

Returning Null or an exception will bring down the client application if not handled correctly but ensures that the error will be noticed or at least handled in some way.

Either approach is acceptable however it is best to be consistent.

share|improve this answer
2  
+1 for "to be consistent". @Dark Star1: If you stay with the current "return null" system you have to add "if (somthing != null)" everywhere. If you change for the new functions to return 0 , "", EmptyList, .... you have to change this everywhere in code..... –  k3b May 31 '12 at 12:36

The answer here lies with the client code. If you can coordinate testing of your changes with the owners of the client code and have them fix any problems exposed or develop functionality to handle changes you have introduced, that would be an adequate solution. If however it is difficult or impossible to coordinate testing then you have to limit or avoid unexpected use of their APIs.

Given more freedom to improve boundary communication you should try to avoid null as an exceptional value, conventions such as -1 as used in, for example, streams are safer but not ideal and sometimes it is difficult to choose a specific value of a type to represent an exceptional case as there could be ambiguities introduced or no particular value available as all values of the type are potentially legitimate input.

High level languages such as Haskell and Scala promote the use of option types to wrap either a value of a particular type or a value of a different type representing an exceptional value or state. Use of option types as parameters forces the client to handle or propagate the option content rather than rely on conventions on specific values or trust that a null won't be received. Option types open up a whole range of useful techniques and patterns.

share|improve this answer

There are many cases where Null is a valid and expected result for a function, and many cases where it is not a valid return value.

For functions that return a reference to "optional" data, then a Null return value can be valid. Examples for this would be reading the column of a database table where Null is allowed. Another example is a reference to a data object that is temporary or defined at a later time.

In those cases, it's programmers error for not checking that the value is set before using the reference.

For functions that should create a new instance of an object, return a reference to a must have object or a function that returns an object that holds the context of the result, then all those cases should always return a valid reference. If it's not possible to do so, then a well documented exception should be thrown.

There are many design patterns out there for handling things in different ways to make it easier for testing or stability.

For example, rather then having a function create a new object to hold a result and return it. The function should take an object as an argument and modify it. Rather then have a constructor that creates objects (which could fail), the constructor should do nothing and have an initialize function with all the required objects as parameters.

share|improve this answer

First of all, as said, the language used leads to differences in the answer. But the main problem is thing is:

  • Returning a "0" value will likely lead to silent errors. Whether this is a special NullObject or 0 or "Jan 1, 1900". At the very least it will compound dirty data problems
  • You are working on an existing product, and existing consumers should remain functional. Not only that, but the rest of the codebase should not regress either.

So, how to handle this?

  • Use a preprocessor (yes, you can do this even when the language is not defined to use one), and create a NULL token that is replaced either by null or 0 based on whether you're in "debug" mode.
  • For external clients, isolate them from the "correct" version, by having the interface it uses become a compatibility interface that (preferably automatically generated) translates between the old one and the new (better one)

This allows you to keep things working as they are for "production", but still "debug" or "test" the system with more correctness. Debug mode would also not use the compatibility layer such that client correctness can be (independently) tested.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.