I was reviewing a coworker's code, and noticed that he was using Integer
objects instead of int
s in some APIs (getters, setters, instance variables, and method parameters). When I asked him why, he told me it was because he wanted to use null
to indicate an uninitialized state, because there was no good default value.
I feel conflicted. On one hand, this is a common practice with other objects (I know, Java 8 makes it better with Optional<T>
). On the other hand, it seems that the chance of a NullPointerException
is higher if API clients use autoboxing.
Is this a good coding practice?
2 Answers 2
This is completely reliant on the problem domain and how the interface is used. I have written interfaces both ways.
I have seen SOAP libraries not allow null values, making Integer
completely unnecessary.
On the flip side, I have used Integer
for cached data (you specifically mention "instance variables" in your question, not just the API):
public class Test implements Serializable {
private transient Integer hash;
public int hashCode() {
if (hash == null) {
hash = <time-consuming hash operation>;
}
return hash.intValue();
}
}
In this case, the object is being used as a key in a HashMap
so it makes sense to cache a hash value rather than recompute it over and over. Using Integer
means I can say with 100% certainty that I have already computed the value or not (as opposed to String
's sentinel value which in theory can produce a hash collision).
If an API accepts Integer but assumes not null, that is a fail-fast scenario. It will throw a NullPointerException
and should do so quickly. Client code using your API should encounter this exception rather quickly, making it easy to find and fix the bad data when testing.
I recommend using Integer
(maybe with Optional to make it clear) for optional data, and int
when it is required.
-
I agree with your answer but I don't understand the reference to
String
's sentinel value. What is that about?5gon12eder– 5gon12eder2016年03月11日 21:22:36 +00:00Commented Mar 11, 2016 at 21:22 -
@5gon12eder
String
uses a value of 0 to indicate there is no hash. It is possible for a string to hash to 0, so it would rehash each time.user22815– user228152016年03月11日 21:37:50 +00:00Commented Mar 11, 2016 at 21:37 -
Is this an implementation detail you've found out (for some implementation) or is it somehow required? I cannot find anything related to this here.5gon12eder– 5gon12eder2016年03月11日 21:45:20 +00:00Commented Mar 11, 2016 at 21:45
-
@5gon12eder implementation detail. Look at
String.java
.user22815– user228152016年03月11日 21:46:16 +00:00Commented Mar 11, 2016 at 21:46
Any value which can ever be null
is a NullPointerException
waiting to happen. Any code which checks those values need to do an additionally check for null
, which is easy to forget for you and even easier to forget for any other users of your classes.
For that reason whenever I have a value which can be intentionally unknown or absent, I use Optional<T>
since Java 8.
Note that using optional.get()
returns NoSuchElementException
on absent values which is only slightly more useful than a NullPointerException
. Instead I prefer the pattern optional.ifPresent( [lambda-expression] );
An alternative solution to avoid NullPointerException
s is the Null Object Pattern. When you have an own class which can be null, also add a singleton which extends that class and represents an uninitialized or unknown instance of that class and which implements all public methods to behave gracefully.