63

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?

Tulains Córdova
  • 39,201
  • 12
  • 97
  • 154
Casey Patton
  • 5,211
  • 7
  • 33
  • 41
  • 3
    See [SO: When to use wrapper class and primitive type](http://stackoverflow.com/questions/1570416/when-to-use-wrapper-class-and-primitive-type) and [SO: Best Practices : What to use, wrapper classes or primitive data types?](http://stackoverflow.com/questions/13902281/best-practices-what-to-use-wrapper-classes-or-primitive-data-types) – Brian Jul 07 '13 at 08: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). – stonemetal Jul 11 '13 at 17:02

5 Answers5

56

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 a Long instead of a long, which means that the program constructs about 2^31 unnecessary Long instances (roughly one for each time the long i is added to the Long sum). Changing the declaration of sum from Long to long reduces the runtime from 43 seconds to 6.8 seconds on my machine.

Random42
  • 10,370
  • 10
  • 48
  • 65
  • 2
    It would help if you listed Bloch's reasons, rather than just quoting his conclusion! – vaughandroid Jul 08 '13 at 12:25
  • @Baqueta I've edited the post. The reason is performance. – Random42 Jul 08 '13 at 12:56
  • That's a bit clearer, thanks. I feel justified in posting my own answer now. :) – vaughandroid Jul 08 '13 at 13:38
  • You might find the [lmax](http://martinfowler.com/articles/lmax.html) 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*." –  Jul 08 '13 at 14:17
  • It's looks like something JIT should handle – deFreitas Jul 01 '18 at 11:40
32

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!

vaughandroid
  • 7,569
  • 4
  • 27
  • 37
  • 2
    Don't suggets people to compare objects with `==`. Objects should be compared with `equals()`. – Tulains Córdova Jul 08 '13 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? – vaughandroid Jul 08 '13 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. – Tulains Córdova Jul 08 '13 at 14:47
  • Good point. Changed it. – vaughandroid Jul 08 '13 at 14:48
  • I added `equals()` to the second code snippet and changed my vote. – Tulains Córdova Jul 08 '13 at 14:51
  • Fun bug (its not a bug in Java, its a bug in how people use Java). Look at the source for `Integer.valueOf(int)` - there's an Integer cache. `==` comparisons work for values within that cache, but not outside. Whats even more fun is that different JREs/JDKs implement the cache differently. –  Jul 08 '13 at 14:53
  • @user61852 Oh, *that* example? I was trying to demonstrate that using the wrappers can be less readable but I couldn't think of a decent trivial example. Your edit has sort of undermined my point... – vaughandroid Jul 08 '13 at 15:10
  • I meant both examples, but edited the second because you already had edited the first one. – Tulains Córdova Jul 08 '13 at 15:15
  • I'll re-edit if I can come up with a better example with 'proper' usage. – vaughandroid Jul 08 '13 at 15:15
  • `// This never gets executed!` is not correct. The statement will be executed because of [pooling](http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.7). – jason Jul 11 '13 at 16:02
  • @Jason Good spot. Updated to use values outside of the pool range. – vaughandroid Jul 11 '13 at 16:32
  • Well, it's outside the *guaranteed* pool range, but compilers can pool those values if they want, too. – jason Jul 11 '13 at 17:48
  • @Jason I wasn't aware of that. Have updated the example again. – vaughandroid Jul 12 '13 at 08:26
12

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.

tobias_k
  • 333
  • 3
  • 12
  • 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, even `null`, 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. – cHao Apr 02 '14 at 06: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`, or `Integer.MAX_VALUE`, or `False`, 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 a `null` value there might be clearer. – tobias_k May 07 '19 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. – cHao May 07 '19 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_k May 07 '19 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.) – cHao May 07 '19 at 14:34
  • @cHao I did not intend this to turn into such a discussion... consider cases where you might use an `Optional` in Java 8, but... well, before Java 8, or if for whatever reason you can not use those. I agree that using `null` comes with it's own bag of problems, but if you _have_ to use some default, or "uninitialized" value, then `null` is probably still better than e.g. `-1` or `false`. – tobias_k May 07 '19 at 14:49
  • What i'm asking is, can you describe a case where 'some default, "unitialized" value' should be allowed to exist? The cases i'm picturing so far, would be better handled by *not having* such a value, and i'm having trouble picturing cases where one is necessary. – cHao May 07 '19 at 17:24
3

In layman's words:

You use the wrappers when you need to add things to collections.

Collections can't hold primitives.

Tulains Córdova
  • 39,201
  • 12
  • 97
  • 154
0

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.

Silvarion
  • 109
  • 3