The short answer is: it depends.
More specifically, the question isn't possible to answer in a general way without seeing the entire class and understanding its design. Null checks are an implementation detail, which are appropriate in certain designs, but might be a smell in others.
In general, methods must live up to their contracts, nothing more. So from the point of view of the API design, this question is irrelevant. You design your API following a long list of existing best practices, and then design your implementation to fit. One of those design best practices is that you shouldn't special case "zero elements" into null
when returning a Collection
. That is, if your method can reasonably return an empty collection, it should return an empty collection, rather than a special value like null
. That almost always simplifies client code and removes a whole class of latent NPEs. This doesn't mean you can't use null
internally to flag "zero elements", but it implies that if you do, you should perform a transformation on return (as in your examples).
So if your getTasks()
method specifies that it always returns a (possibly empty) list, using a null check as in your example is a reasonable implementation choice. In your specific examples it may not be the best choice, since the JDK offers methods like Collections.emptyList<T>()
, which allow you to cheaply re-use a singleton empty list instance, so you aren't saving anything in memory, and CPU savings are questionable1.
Furthermore, by using the "null
means empty list" convention, you require that all internal methods either have to check for tasks == null
, or else use the getTasks() method. In the specific case of collection objects, I usually prefer to use the Collections.empty*
methods rather that null to represent empty lists2. Similar reasoning applies to the Integer
case since Integer.valueOf(0)
returns a singleton object on all compliant JVMs.
Those specific case aside, there are certainly reasons to use null checks to represent a missing or default element, particular when no good "empty" element presents itself or where performance is important. You will find this pattern used somewhat frequently across well designed libraries such as the Java runtime and Guava. So you can't say it's a bad practice. It is a practice to be carefully evaluated.
1Use of these Collection.empty*
methods applies also to your example. In your null case, you may consider directly returning Collections.emptyList()
, rather than creating a new ArrayList
on-demand. Whether that is appropriate depends on the overall class design, e.g., whether that list is mutable.
2Whether Collections.emptyList()
is faster than an explicit null check is actually a complex question. If empty lists are so uncommon that they usually don't occur in a given process, the emptyList()
approach is likely faster, since its cost (wrt the getter) goes to zero as the frequency of empty lists goes to zero. On the other hand, when the empty list does occur, it may create a polymorphic call site for all methods that use the List
: every such site may see both a special "empty list" implementation and ArrayList
. This can slow down the code, although a lot depends on inlining decisions. So, as usual with performance considerations, the exact answer is complex and you need to profile if the details matter.