Forums Register Login

primitives vs boxed types

+Pie Number of slices to send: Send
I know that some modern languages do not have primitives. I read once that removing primitives was proposed as a future update in Java, possibly as early as Java 9 or 10. I don't think that is going to happen because a lot of people still need that for performance reasons and it would break backwards compatibility. But if that notion can even be proposed, it suggests that someone feels that using primitives in normal code should be avoided.

I know there are some performance issues, and some memory space issues. But I try not to fall victim to premature optimization. So for the most part this is a draw.

On the other hand, I do strive for clarity of code. Which of these pairs of lines is most clear?

Neither pair is hard to read, but I would say the primitives are cleaner. I would say that is one point for primitives. (Now before someone flames me, I know I could have written the second pair like this)

But I think that is just syntactic sugar for the original writing. I feel like it sort of disguises the fact that I am really dealing with objects here. But if you do that it means that, then code clarity is the same for both.


Obviously primitives and generics don't mix. So you end up with parallel amalgamation structures, Arrays and Lists respectively. And while we are at it, Arrays and Generics especially don't mix, and Bloch's book recommends preferring Lists to Arrays. Does that mean I should prefer boxed types primitives? I would say so. So maybe one point for boxed types.

But I have never been bitten by having made the decision to use a boolean instead a Boolean except when I wasn't consistent. And when I look at the libraries in the JDK I see that many functions accept and return primitive types. They could have returned boxed types. But they, in their wisdom, decided against it. That for me would be one point for primitives.


I am not really keeping score here. What I am looking for is someone who can make better arguments for one or the other than the ones I just presented. Maybe they could relate some experiences that leads them to strongly favor one over the other. Or is this really all just opinion and style?



+Pie Number of slices to send: Send
IMHO removing primitives would provide a way to also remove a lot of exceptions and things that you have to count with. If it's handled exceptionally by compiler doesn't mather that much, but if a developer can handle everything in a uniform way, the code becomes cleaner as well.
1
+Pie Number of slices to send: Send
 

Derik Davenport wrote:I read once that removing primitives was proposed as a future update in Java, possibly as early as Java 9 or 10. I don't think that is going to happen because a lot of people still need that for performance reasons and it would break backwards compatibility.


The idea is to remove primitives from the Java programming language, but not from the JVM. Such a radical change would indeed make the language incompatible with previous versions, so it will probably never happen in Java. It does not have anything to do with performance - not having primitives in the language doesn't mean that they don't exist at the virtual machine level. Without primitives in the language, the compiler would still generate bytecode that uses primitives on the JVM.

My rule of thumb is: Use primitives, unless there is a reason why you should use wrapper classes (such as Integer instead of int). Why: Because primitives are more efficient. An instance of a wrapper class is an object, which takes up much more memory than a primitive.

Also, use auto-boxing instead of explicitly creating instances of wrapper classes. So:



Why creating instances of wrapper classes is bad: Class Integer (and also some of the other wrapper classes) have a cache of objects, I think Integer has by default a cache containing all Integer objects with the values between -127 and 128. If you call Integer y = Integer.valueOf(7); then you get the cached Integer object. If you call Integer y = new Integer(7); then you are always (unnecessarily) creating a new Integer object. Note that auto-boxing uses Integer.valueOf(...), so Integer y = 7; is translated to Integer y = Integer.valueOf(7);.

Derik Davenport wrote:Obviously primitives and generics don't mix. So you end up with parallel amalgamation structures, Arrays and Lists respectively. And while we are at it, Arrays and Generics especially don't mix, and Bloch's book recommends preferring Lists to Arrays. Does that mean I should prefer boxed types primitives? I would say so. So maybe one point for boxed types.


You can't use primitives for generic type parameters, and that is the main (maybe even the only) reason why the wrapper classes exist. This might be addressed in a future version of Java, so that we can have Lists of ints without boxing.
2
+Pie Number of slices to send: Send
Something more about not having primitives at the language level:

Primitives in the Java programming language are an example of a leaky abstraction. They are a feature from a lower level (the virtual machine level) that shows up at a higher level (the programming language level).

All kinds of systems have levels of abstraction. A higher level hides the details of a lower level, which makes the higher level useful - you don't want to have to think about the details of the lower level all the time. Imagine that when you write a program, you'd have to think what happens exactly in the virtual machine, the CPU, the transistors, the electrons etc. when that program runs. You'd never be able to finish your program because you'd need too much time to think all the details through. So, that's why we abstract the details away.

The JVM and the CPU work with primitive values. A CPU has a handful of registers that can hold values, such as integers of various sizes (64-bit, 32-bit, etc.).

Presumably to keep the design of the Java compiler simple, primitive types show up directly in the Java programming language. This makes the programming language less object-oriented - in a pure object-oriented language, everything should be an object, and primitives are not objects.

The designers of the programming language could have hidden primitives from the programmer, and make it so that everything looks like an object. A number of more modern programming languages do this, for example Scala. In Scala there are no primitives in the language - instead of int there is scala.Int, for example, which programs like it is an object (you can call methods on it etc.). However, whenever you use scala.Int, the compiler translates it to a primitive int at the virtual machine level.

It would have been nice if the designers of the Java programming language had thought of this in the 1990's when Java was invented...
+Pie Number of slices to send: Send
 

Derik Davenport wrote:But if that notion can even be proposed, it suggests that someone feels that using primitives in normal code should be avoided.


Which would be extremely difficult.

The fact of the matter is that an Integer is NOT an int; and doesn't even behave like one - as you'll soon discover if you ever try to write:
  Integer y = 7;
  ++y;

[Edit: Ooof. I've just discovered that the above is perfectly valid. See my comments below.]

The fact is, I've always been a bit dubious about "autoboxing", and feel that it should at least be accompanied by a compiler warning (along with an equivalent "Suppress" or "SafeBox" annotation). There are obviously cases where it makes sense (as with the assignment above), but what about:
  if (array[y] == null) ...
?

Indeed, what about all those int[] and char[]s that we currently use in our programs?

I agree with everything that Jesper has said; I just think it's too late to fix it now.

Winston

[Edit] Having discovered that '++' IS valid on an Integer, I redouble my objection to autoboxing.

The only way that could be possible is by the compiler silently changing the '++' to something like:
  y = Integer.valueOf(y.intValue() + 1);
and to me, to do that WITHOUT a warning is criminal. Suppose I now wrote:
  for (y = 0; y < 10000; y++) { ...
(which also compiles without a warning) can you imagine how much extra code I'm generating...?
+Pie Number of slices to send: Send
 

Jesper de Jong wrote: . . .
You can't use primitives for generic type parameters, and that is the main (maybe even the only) reason why the wrapper classes exist. . . .

But the eight wrapper classes were written before generics were (re)introduced into Java®. I think they had to be written because there was a need for methods operating on primitives and so one could store the equivalent of primitives in Collections.

In C# I believe they used to use primitives but have changed the language so all values are exposed to the user as objects.
+Pie Number of slices to send: Send
 

Campbell Ritchie wrote:In C# I believe they used to use primitives but have changed the language so all values are exposed to the user as objects.


Which I have to admit I rather liked. But are C#'s wrappers mutable? It seems to me that it makes a huge difference.

I remember an example from Effective Java of "hidden autoboxing" and I seem to remember JB saying that it took something like 10 times as long to execute. now for single operations, that isn't a problem; but when collections or loops get involved, you could create some very inefficient code.

But hey, maybe the compiler is smarter than I give it credit for...

Winston
+Pie Number of slices to send: Send
The example is from Effective Java™ by Bloch page 221-3. JB says there are several problems with boxed types. One is that the == operator does not operate on intValue() (for Integers) or similar but looks for reference identity.
Also Bloch says you can't use default values; the default value for an Integer field is null, so any attempt to unbox that will cause a NullPointerException.
Also you have the performance problem. Bloch's example took 1.7s with longs and 14s with Longs when I tried it a few seconds ago.
1
+Pie Number of slices to send: Send
 

Winston Gutkowski wrote:
But hey, maybe the compiler is smarter than I give it credit for...

Winston



The compiler is doing exactly what you think it is...just look at the byte code for your ++y example above.
It is indeed, unboxing, incrementing and reboxing.

The loop example is equally "entertaining".
+Pie Number of slices to send: Send
 

Dave Tolls wrote: . . . The compiler is . . . unboxing, incrementing and reboxing. . . .

Surely that is the same as the JLS says?
+Pie Number of slices to send: Send
 

Campbell Ritchie wrote:Surely that is the same as the JLS says?


Very possibly. I just don't like the fact that they've changed a supposedly immutable object into a "mutable" one. And it's done silently.

Probably barking at the moon, but I would have allowed it for cases involving a single call (either valueOf() or intValue()) per instance, and disallowed it for more "involved" stuff. But I suspect that would have meant more ways for it to go wrong, so I say: add a compiler warning.

Winston
+Pie Number of slices to send: Send
I am getting confused here. Integer remains immutable in Java®. This is an extract from the JDK8u25 src.zip file.It only has one field and the class is declared final.

Maybe you meant that C# int objects might be mutable.
If you writeThe line 2 involves unboxing, addition and reboxing, but there is also an assignment step. The object pointed to by iii in line 1 is unchanged, but iii is reassigned to a new object in line 2. So that sort of code does not violate immutability.
+Pie Number of slices to send: Send
 

Campbell Ritchie wrote:I am getting confused here. Integer remains immutable in Java®...


I think you're taking me too literally. What I mean is that allowing the '++' operator (not to mention, presumably, '±=' or any other "compound assignment") on an Integer makes it look mutable; and that, to me, is wrong. Basically, the compiler is adding a pile of code behind the scenes to make an Integer behave like something it isn't.

And, as I've said, my main complaint is not so much that it does it; but that it does it silently - even in cases that I, for one, didn't expect it to do it at all.

Hope I've explained myself.

Winston
+Pie Number of slices to send: Send
 

Winston Gutkowski wrote:And, as I've said, my main complaint is not so much that it does it; but that it does it silently - even in cases that I, for one, didn't expect it to do it at all.


I also reckon that it could be a very arcane source of "memory leaks" , since '++i' creates a new object without telling you. Of the top of my head I can't think of a scenario where it could cause problems, except maybe a big loop; but the mere fact that it can happen is enough to say to me that it wasn't the Java Gods' smartest decision.

Winston
+Pie Number of slices to send: Send
 

Winston Gutkowski wrote: . . .
Hope I've explained myself. . . .

Yes, you have.
+Pie Number of slices to send: Send
You get different hash codes, showing that the two objects are different instances. How could you get a memory leak? The first Integer instance is (unless it is stored elsewhere) eligible for garbage collection.

I don't think it is a case of anything being done “silently”; if anything it is much more serious.
+Pie Number of slices to send: Send
 

A few minutes ago, I wrote: . . . much more serious.

The JVM is programmed on the assumption its users know how it works

It assumes in this instance that people know Integer is immutable and using += or ++ creates a new instance. The Java® Language Specification says that one is added to the value, and the value of i++ is the same as the old expression. It also says that it may undergo boxing conversion. Note that boxing conversion produces the same Integer for values −128…127 and maybe other values.
+Pie Number of slices to send: Send
 

Winston Gutkowski wrote:I redouble my objection to autoboxing.




I ask myself whether autoboxing creates more problems than it solves.
+Pie Number of slices to send: Send
I think we end up going back to the earlier post with the quotes from Bloch's book. Maybe we need a new design principle

Always use primitives for arithmetic, not boxed objects.

 
+Pie Number of slices to send: Send
 

Ivan Jozsef Balazs wrote:I ask myself whether autoboxing creates more problems than it solves.


It's a tough one, because there are cases where it makes a lot of sense. Straight boxing for assignments and parameter conversions seems perfectly reasonable to me, and "vanilla" unboxing too; but not arithmetic or anything that involves boxing AND unboxing in the same "operation".

Winston
+Pie Number of slices to send: Send
 

Campbell Ritchie wrote:I think we end up going back to the earlier post with the quotes from Bloch's book. Maybe we need a new design principle

Always use primitives for arithmetic, not boxed objects.

This was exactly the conversation I wanted to read. A bunch of experts talking about the ramifications of using boxed vs primitive types. I feel humbled. Truly.

Cambell, do you think that design rule implies that all other activities should be done with boxed types?

Some comments on what other have said....
Something like Boolean has only 3 states (including null) and the OS should more or less never use more space than that. And you don't do arithmetic with them, only boolean logic and that can be done equally well with Boolean.TRUE and Boolean.FALSE as with booleans I would think. This seems like a pretty clear case where the compiler could make a boxed type every bit as flexible as a primitive.

As for Integers, I can see how that would pile up short lived instances of small classes if used as a counting variable in a long loop.

Campbell Ritchie wrote:Bloch's example took 1.7s with longs and 14s with Longs when I tried it a few seconds ago.


But I am working hard to avoid thinking about those kinds of performance issues. It is rough for me because I come from a background in embedded programming where those kinds of issues were paramount! Most of the time you are NOT in loops. When ExecutorService returns the number of active threads it is returning a number that is almost certainly less than 1000, and it is highly unlikely that the caller would be looping on that result anyway. And so what if he did? Is it unfair to make caller responsible for unboxing the result for performance reasons if, and only if, that was necessary in his environment and/or his application?


Better still, wouldn't be reasonable for the programmer to expect that the compiler to take the liberty of converting an Integer to an int in a loop? After all, it can take the liberty of promoting things out of the loop and making other structural changes if it can be sure it will not affect the result (within the confines of a single thread). If the compilers make the move first, it should not hurt existing code. Perhaps that is what Jesper meant when he said

Jesper de Jong wrote:The idea is to remove primitives from the Java programming language, but not from the JVM.



Campbell Ritchie, quoting J. Bloch wrote:... the == operator does not operate on intValue() (for Integers) or similar but looks for reference identity.
Also Bloch says you can't use default values; the default value for an Integer field is null, so any attempt to unbox that will cause a NullPointerException.

This would suggest that more syntactic sugar is needed. So if ++myInteger works, and I think someone suggested that += and *= also work, then why now allow "==" on Integer to compare the values of the Integer? I realize that means you have just overloaded the == operator, but if you can overload the others, why not this? Well the could possibly a problem where you REALLY wanted to compare if two instances of Integer were really the same instance, but in that rare circumstance it would have been sufficient to cast one or the other to Object before doing the compare. But I haven't thought this through, maybe there is some huge problem with that idea. There would probably be objections if we tried to do it now!

Winston Gutkowski wrote:I just don't like the fact that they've changed a supposedly immutable object into a "mutable" one. And it's done silently.

Why? Didn't we do the same with String? Specifically I can write
Line 5 sure makes it look like String a is mutable. We have already broken the rules in an effort to make String immutable. For starters we overloaded the + operator! Maybe they could have done the same with Integers and given them the same type of special handling given to String (a courtesy to the programmer). But it might be hard to do it now after the fact.


but not arithmetic or anything that involves boxing AND unboxing in the same "operation"

Perhaps there should indeed be a warning about that, just like the warning you get when comparing String with ==.
+Pie Number of slices to send: Send
 

Derik Davenport wrote: . . .
Cambell, do you think that design rule implies that all other activities should be done with boxed types?
. . .

I don't think anything about that design principle, since I created it. I only know things about it (I am sure thousands of other people have created virtually identical rules). That doesn't say anything about boxing for adding to Lists, so this sort of thing is all right:I am sure that is what boxing was intended for.
+Pie Number of slices to send: Send
 

Derik Davenport wrote: . . . So if ++myInteger works, and I think someone suggested that += and *= also work, then why now allow "==" on Integer to compare the values of the Integer? I realize that means you have just overloaded the == operator, but if you can overload the others, why not this? . . . But it might be hard to do it now after the fact.

Some languages so that sort of thing: the == operator is overloaded to check for String equality rather than identity in C#, but as you say it would be hard to do after the fact.

Whoever upgraded Java™ eleven years ago obviously made the decision not to overload ==.

but not arithmetic or anything that involves boxing AND unboxing in the same "operation"

Perhaps there should indeed be a warning about that, just like the warning you get when comparing String with ==.

Where do you get that warning? Is it from an IDE? I don't think the javac tool provides such warnings.
+Pie Number of slices to send: Send
 

Derik Davenport wrote: . . . I can write
Line 5 sure makes it look like String a is mutable. . . .

No, that is not mutability. This is what mutability would look like:-Remember that most built-in immutable non‑wrapper classes have mutable counterparts; in this case the mutable counterpart would be StringBuilder.
+Pie Number of slices to send: Send
 

Derik Davenport wrote:Didn't we do the same with String? Specifically I can write...


Sure you can; and I'm not wild about that either. But at least it's been around since version 1, and only involves one operator. Autoboxing was introduced in version 5 (I think), and involves ALL arithmetic, bitwise AND comparison operators when used on numeric wrappers; not to mention '[int]'. It's also not as well explained in most books/tutorials I've seen.

And just FYI, '+' for Strings still causes confusion. Specifically, scads of questions on these forums about whether to use '+' or a StringBuilder when you have lots of 'em.

Winston
1
+Pie Number of slices to send: Send
 

Winston Gutkowski wrote: . . . not arithmetic or anything that involves boxing AND unboxing in the same "operation". . . .

That looks very similar to




Always use primitives for arithmetic, not boxed objects.
+Pie Number of slices to send: Send
 

Campbell Ritchie wrote:No, that is not mutability. This is what mutability would look like:-

Yeah, you are right. I was thinking about that on the way home. YOurs is a much better example of what mutability look like. No one would expect my assert to pass.

I don't think anything about that design principle, since I created it.

I choose my wording very carefully. Your introduction to that design principle was a bit vague. You said "I think we end up going back to the earlier post with the quotes from Bloch's book. Maybe we need a new design principle" That sounded to me as if you might have been attributing those words to Bloch, and also possibly some earlier post. I couldn't find such a quote in his book, but didn't look very hard. . I think your principle is valid, but it doesn't say much outside the context of this discussion. For instance, it doesn't say anything about using (or not using) boxed types at any other time. What I am looking for is a design principle that tells me (and other programmers) how to design new API's.

From this discussion it really seems like the boxed types are really only a problem in 2 circumstances.
1) Where you need mutability to avoid creating thousands of short lived instances.
2) Where just going through the process of dereferencing the value could affect performance.

If you do not engage in premature optimisation, then you probably shouldn't be worried about either of these. And if compilers were smart enough to recognize this issue, then they should able to compile this out anyway as long as primitives are a part of the JVM. Which leads me to this kind of rule.

Use boxed types for all variables EXCEPT where performance requires that you use primitives.

That said, I have since looked at some more APIs (including the Guava libraries) and it is almost a universal practice to build API's with primitves. Indeed, within the implementation code I saw, most programmers seem to use the following rule.

use primitives for all variables EXCEPT where they must interact with generic types.

. My first rule looks good on paper, but even I wouldn't advocate it given the overwhelming precedent to the contrary.




+Pie Number of slices to send: Send
 

Derik Davenport wrote: . . . Use boxed types for all variables EXCEPT where performance requires that you use primitives. . . .

I know we can go round and round in circles agreeing with one another, but what about the == operator. Bloch (loc cit) gives an example where the behaviour of < > and == on an Integer object gives unexpected and incorrect results.
And the counter example (not counterexample) in the Java® Tutorials shamelessly does arithmetic on an Integer object because that is what is retrieved from the Map in question. So my suggestion may not be of universal applicability.
+Pie Number of slices to send: Send
My reasoning about this particular issue is a simplistic one... I see it as a simple design issue that came up in the language caused by introducing generic programming without breaking compatibility with older code bases...

Before generics the language used inheritance to implement generic code using the Object class which caused problems at runtime... So in order to promote type-safety they simply wrapped the primitives and erased away the generic type notation at compile time which allowed generic programming with type safety...

So to my understanding of things, one should only concern him/herself about distinguishing between the both only when dealing with some generic type as the purpose of it all was to allow transparency, which mean that it was fundamental to allow all types within the language to work with the notion of generic programming...
+Pie Number of slices to send: Send
 

Rico Felix wrote: . . .
Before generics the language used inheritance to implement generic code . . .

You mean in the bad old days before they got their act together to implement generics which other languages had implemented from the beginning, you could only put Objects into Collections. The problem arose because one had to cast the Objects retrieved and that was not type‑safe. Boxing allows one to appear to add primitives to Collections (as I showed earlier).
+Pie Number of slices to send: Send
 

Campbell Ritchie wrote:Boxing allows one to appear to add primitives to Collections (as I showed earlier).


Exactly...
+Pie Number of slices to send: Send
That is what boxing is good for.
+Pie Number of slices to send: Send
 

Campbell Ritchie wrote:That looks very similar to … Always use primitives for arithmetic, not boxed objects.


and incrementing, and bitwise ops, and compound assignments, and ...

BTW, what does
  if (myInteger == 7)
do?

Box the literal, or unbox the object? I hope it would be the latter (and I'm sure it's specified somewhere), but the fact is that I don't know ... and I don't really want to have to know either.

Winston
+Pie Number of slices to send: Send
If you compare a primitive with an object, the object is always unboxed. That can lead to some surprising NullPointerExceptions.
+Pie Number of slices to send: Send
 

Campbell Ritchie wrote:Bloch (loc cit) gives an example where the behaviour of < > and == on an Integer object gives unexpected and incorrect results.

Rob Spoor wrote:If you compare a primitive with an object, the object is always unboxed. That can lead to some surprising NullPointerExceptions.

Perhaps more syntactic sugar should have been made to address this at the time of release. For example, they could have made it an error to create an instance of [Number] that was null. They could have overloaded == when 2 boxed types were involved. But maybe that was impossible for other reasons I haven't considered.

but the fact is that I don't know ... and I don't really want to have to know either


In my own code, I might make an explicit cast on the object. The cast makes it clear what the original programmer intended, and it is a nice "heads up" to whomever reads your code 3 years from now that some variables are not primitives. As Rob pointed out, there is an implicit cast, but the advantages of making it explicit outweigh the 5 keystrokes of "(int)".
+Pie Number of slices to send: Send
Forbidding to make Integer, Long, etc null would break a lot of code. Similarly, changing the meaning of == for wrapper types would break code that depends on reference equality instead of value equality. In other words, they couldn't do either.
1
+Pie Number of slices to send: Send
 

Rob Spoor wrote:In other words, they couldn't do either.


And isn't it interesting that offering something as "simple" as autoboxing - which probably seems (or seemed) like a really good idea - is fraught with so many difficulties?

You know what they say - and it applies to designers probably more than anyone - Want know how to make God laugh? Tell Him your plans...

Winston
+Pie Number of slices to send: Send
 

Rob Spoor wrote:In other words, they couldn't do either.

Absolutely. They can't do either of them now. But if they had done them at the time they implemented the wrapper classes, then they could have done this. Sadly, until they implemented autoboxing the value of this would not have been apparent.

Winston Gutkowski wrote:And isn't it interesting that offering something as "simple" as autoboxing...is fraught with so many difficulties?

Oh man that is so true. So very, very true.
Hold that thought. Tiny ad:
a bit of art, as a gift, that will fit in a stocking
https://gardener-gift.com


reply
reply
This thread has been viewed 2746 times.
Similar Threads
Tic-Tac-Toe Bugs -solved-
Simple J2SE 5.0 Tiger Notes
5 Golden Rules of widening, boxing & varargs
Can your object override the = instead of .equals?
Run - Compile Time Error ? Upcasting Downcasting
A better way to design TicTacToe
More...

All times above are in ranch (not your local) time.
The current ranch time is
Mar 28, 2024 02:49:38.