I see that Java has Boolean (class) vs boolean (primitive). Likewise, there's an Integer (class) vs int (primitive). What's the best practice on when to use the primitive version vs the class? Should I basically always be using the class version unless I have a specific (performance?) reason not to? What's the most common, accepted way to use each?
-
3See SO: When to use wrapper class and primitive type and SO: Best Practices : What to use, wrapper classes or primitive data types?– BrianCommented Jul 7, 2013 at 8:45
-
To my mind those aren't classes, they are boxes. They are only there so you can use primitives where objects are required, i.e. in collections. You can't add two Integers(you can pretend but really java is auto boxing|unboxing the values for you).– stonemetalCommented Jul 11, 2013 at 17:02
5 Answers
In Item 5, of Effective Java, Joshua Bloch says
The lesson is clear: prefer primitives to boxed primitives, and watch out for unintentional autoboxing.
One good use for classes is when using them as generic types (including Collection classes, such as lists and maps) or when you want to transform them to other type without implicit casting (for example Integer
class has methods doubleValue()
or byteValue()
.
Edit: Joshua Bloch's reason is:
// Hideously slow program! Can you spot the object creation? public static void main(String[] args) { Long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i; } System.out.println(sum); }
This program gets the right answer, but it is much slower than it should be, due to a one-character typographical error. The variable
sum
is declared as aLong
instead of along
, which means that the program constructs about 2^31 unnecessaryLong
instances (roughly one for each time thelong i
is added to theLong sum
). Changing the declaration of sum fromLong
tolong
reduces the runtime from 43 seconds to 6.8 seconds on my machine.
-
2It would help if you listed Bloch's reasons, rather than just quoting his conclusion! Commented Jul 8, 2013 at 12:25
-
-
That's a bit clearer, thanks. I feel justified in posting my own answer now. :) Commented Jul 8, 2013 at 13:38
-
You might find the lmax architecture interesting - "It took a bit more cleverness to go up another order of magnitude. There are several things that the LMAX team found helpful to get there. One was to write custom implementations of the java collections that were designed to be cache-friendly and careful with garbage. An example of this is using primitive java longs as hashmap keys with a specially written array backed Map implementation."– user40980Commented Jul 8, 2013 at 14:17
-
The standard practice is to go with the primitives, unless you're dealing with generics (make sure you are aware of autoboxing & unboxing!).
There are a number of good reasons to follow the convention:
1. You avoid simple mistakes:
There are some subtle, non-intuitive cases which often catch out beginners. Even experienced coders slip up and make these mistakes sometimes (hopefully this will be followed by swearing when they debug the code and find the error!).
The most commmon mistake is using a == b
instead of a.equals(b)
. People are used to doing a == b
with primitives so it's easily done when you're using the Object wrappers.
Integer a = new Integer(2);
Integer b = new Integer(2);
if (a == b) { // Should be a.equals(b)
// This never gets executed.
}
Integer c = Integer.valueOf(2);
Integer d = Integer.valueOf(2);
if (c == d) { // Should be a.equals(b), but happens to work with these particular values!
// This will get executed
}
Integer e = 1000;
Integer f = 1000;
if (e == f) { // Should be a.equals(b)
// Whether this gets executed depends on which compiler you use!
}
2. Readability:
Consider the following two examples. Most people would say the second is more readable.
Integer a = 2;
Integer b = 2;
if (!a.equals(b)) {
// ...
}
int c = 2;
int d = 2;
if (c != d) {
// ...
}
3. Performance:
The fact is it is slower to use the Object wrappers for primitives than just using the primitives. You're adding the cost of object instantiation, method calls, etc. to things that you use all over the place.
Knuth's "...say about 97% of the time: premature optimization is the root of all evil" quote doesn't really apply here. He was talking about optimisations which make the code (or system) more complicated - if you agree with point #2, this is an optimization which makes the code less complicated!
4. It's the convention:
If you make different stylistic choices to 99% of the other Java programmers out there, there are 2 downsides:
- You will find other people's code harder to read. 99% of the examples/tutorials/etc out there will use primitives. Whenever you read one you'll have the extra cognitive overhead of thinking about how it would look in the style that you're used to.
- Other people will find your code harder to read. Whenever you ask questions on Stack Overflow you'll have to sift through answers/comments asking "why aren't you using primitives?". If you don't believe me, just look at the battles people have over things like bracket placement, which doesn't even affect the generated code!
Normally I'd list some counter-points, but I honestly can't think of any good reasons to not go with the convention here!
-
2Don't suggets people to compare objects with
==
. Objects should be compared withequals()
. Commented Jul 8, 2013 at 14:35 -
2@user61852 I wasn't suggesting that as a thing to do, but as a common mistake that is made! Should I make that clearer? Commented Jul 8, 2013 at 14:43
-
Yes, you don't mention that objects shoud be compared with
equals()
... you give them a workaround so that comparing objects with==
yields the expected results. Commented Jul 8, 2013 at 14:47 -
-
I added
equals()
to the second code snippet and changed my vote. Commented Jul 8, 2013 at 14:51
Usually I go with the primitives. However, one peculiarity of using classes like Integer
and Boolean
is the possibility of assigning null
to those variables. Of course, this means that you have to do null
checks all the time, but still better to get a NullPointerException than to have logic errors due to using some int
or boolean
variable that has not been initialized correctly.
Of course, since Java 8 you can (and probably should) go one step further and instead of e.g. Integer
you could use Optional<Integer>
for variables that might or might not have a value.
Additionally, it introduces the possibility to use null
to assign those variables an "unknown" or "wildcard" value. This can be handy in some situations, for example, in Ternary Logic. Or you might want to check whether a certain object matches some template; in this case you can use null
for those variables in the template that can have any value in the object.
-
2(Wasn't my downvote, but...) Java already detects uninitialized variables, and won't let you read a variable til every code path leading up to its use has definitively assigned a value. So you don't gain much from the ability to assign
null
as a default. On the contrary: you'd be better off not "initializing" the variable at all. Setting any default value, evennull
, shuts up the compiler... but also keeps it from detecting the lack of useful assignment along all code paths. Thus, an error the compiler could have caught, slips through to runtime.– cHaoCommented Apr 2, 2014 at 6:37 -
1@cHao But what if there is no sensible default value to initialize the variable with? You could set it to
0.0
, or-1
, orInteger.MAX_VALUE
, orFalse
, but in the end you do not know if that's the default value, or an actual value that was assigned to that variable. In cases where this is important, having anull
value there might be clearer.– tobias_kCommented May 7, 2019 at 11:10 -
It's not clearer, it's just easier to tell the compiler not to warn you about unclear logic until a bug has already propagated. :P In cases where there's no sensible default, don't initialize the variable. Only set it once you have a sensible value to put there. This lets Java stop you at compile time if your value might not be set correctly.– cHaoCommented May 7, 2019 at 12:30
-
@cHao I meant, there might be cases where you can not initialize the variable and you have to deal with it at runtime. In such cases, a "default" like "null", that can clearly be identified as being the default, or "uninitilized", might be better than any compile-time initialization that might also be a valid value.– tobias_kCommented May 7, 2019 at 12:56
-
Do you have such a case in mind? The cases that i can think of are at interface boundaries (eg: as parameter or return types)...but even there, they'd be far better if wrapped. (Naked
null
s come with a whole host of problems, including null paranoia.) Within a function, a variable that might actually be uninitialized at the point of use typically indicates uncovered cases. (Definite assignment analysis is simplistic, so false positives are possible. But you can often resolve them by simplifying the logic, so.)– cHaoCommented May 7, 2019 at 14:34
In layman's words:
You use the wrappers when you need to add things to collections.
Collections can't hold primitives.
Java features autoboxing as m3th0dman pointed out. Think about in the lowest level possible and you'ĺl see that autoboxing (in or out) a primitive value will imply clock cycles spent in some tasks you don't need if you're working with native data types around your application.
As a general rule, you should try to use native data types whenever possible.