• Post Reply Bookmark Topic Watch Topic
  • New Topic

Autoboxing - Good or Bad?  RSS feed

 
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
[Winston] I split this discussion off from this thead because it seemed to be developing a life of its own. Hope you don't mind.


Roel De Nijs wrote:In my opinion it definitely should be for several reasons.

Oh goodee, a "philosophy" debate.

(1) It keeps things simple and clean. With one (simple) sentence you can explain the rule (without having to add an "unless both are denoting wrapper class instances" clause): when both operands are reference variables, the == operator will return true if both refer to the same object.
True, but the operative phrase there is "when both operands are reference variables". Clearly things are not as simple as you would suggest when they are not, because now one of those operators can be "boxed". What doesn't happen - and would seem more logical to both me and Ramya, is that one of them is unboxed.

(2) If try to compare incompatible types (e.g. Cat and Dog, Short and Integer,...) using the == operator, you'll get a compiler error. But if both (wrapper class) operands are unboxed, incompatible types (with the same primitive value) become equal
ie, after unboxing, they compare exactly as they would for a primitive comparison.

And question: If what you say above is true, then shouldn't '1L == Integer.valueOf(1)' be an error? Particularly when both
  Long i = 1;
and
  Integer i = 1L;
are.
But it ain't. And moreover it returns a value comparison (I tested it with 832745 just to make sure ).

(3) Related to (1) and (2), operator overloading in Java is limited to a strict minimum (only the + operator) and I think that helps to keep a programming language simple and clean. If the behavior is different depending on the operand types, the == operator would be overloaded as well.
Hmmm. Kind of agree, but to me it's a price I'd be willing to pay for proper autoboxing.

(4) What if you (for some weird/strange business requirement) you need to check if both reference variables refer to the same object. If it's always unboxed, you can't perform this check anymore. Now you have the choice: use reference variables for reference equality or use primitiveTypeValue() methods for primitive value equality.
I get your point, but we are talking the compiler here, so why couldn't this be done based on the declared type? That's how it works for passing parameters.

I reckon it's far more likely that they chose not to do it because, in the bizarre situation you cite above, it violates backward-compatibility - ie, a program that relies on '==' checking whether two Integers are the same object will no longer work the way it used to.

But I'm barking at the moon, I know. It's done now, and we have to live with it. Looking back though, especially in light of the number of questions it raises on these forums, it feels to me like a kludge that was the best they could get away with given other orthogonal constraints.

Winston
 
Sheriff
Posts: 11338
177
AngularJS Chrome Eclipse IDE Hibernate Java jQuery MySQL Database Spring Tomcat Server
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Winston Gutkowski wrote:And question: If what you say above is true, then shouldn't '1L == Integer.valueOf(1)' be an error? Particularly when both
  Long i = 1;
and
  Integer i = 1L;
are.
But it ain't. And moreover it returns a value comparison (I tested it with 832745 just to make sure ).

If you compare incompatible types using the == operator, you'll always get a compiler error. In the statement 1L == Integer.valueOf(1) you don't have incompatible types from Java 5.0 onwards. You can compare a primitive value and a wrapper class instance using the == operator (thanks to unboxing of the wrapper class instance). Prior to Java 5.0 it would be incompatible types.

In this code snippet you'll have a comparison of two incompatible types, resulting in a compiler errorIf you would unbox both operands to perform a primitive comparison, you'll be ignoring the type of the instance. It's like a Dog and a Cat are exactly the same if both have the same name. In fact, the equals(Object o) method of the wrapper classes will only return true if both objects have the same type and contain the same value.
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Roel De Nijs wrote:If you compare incompatible types using the == operator, you'll always get a compiler error. In the statement 1L == Integer.valueOf(1) you don't have incompatible types from Java 5.0 onwards. You can compare a primitive value and a wrapper class instance using the == operator (thanks to unboxing of the wrapper class instance). Prior to Java 5.0 it would be incompatible types.

But if you can unbox one, then why not both? It seems to me it would save a lot of confusion.

As I say, the only answer I can think of is to preserve backward-compatibility for the one case that nobody in their right mind would use '==' for anyway.

And unfortunately, the JLS doesn't do much to clarify the situation because the first thing it says on the subject (15.21) is:
"The equality operators may be used to compare two operands that are convertible (§5.1.8) to numeric type, or two operands of type boolean or Boolean, or two operands that are each of either reference type or the null type. All other cases result in a compile-time error."
(5.1.8 is the paragraph for unboxing conversion)

In the next paragraph (15.21.1) it does say:
"If the operands of an equality operator are both of numeric type, or one is of numeric type and the other is convertible (§5.1.8) to numeric type, binary numeric promotion is performed on the operands (§5.6.2)."

But it's very easy to miss out the fact that basically only one of the operands is allowed to be convertible until you get to 15.21.3, which says:
"If the operands of an equality operator are both of either reference type or the null type, then the operation is object equality."
Thanks guys, you could have told me that three paragraphs back.

And 5.6.2 (binary promotion, which can involve unboxing) is no clearer about it either. If you just read that para you could easily assume that both operands can be convertible.

Basically, it's just not very well written.

Winston
 
author
Bartender
Posts: 4093
21
Eclipse IDE Flex Google Web Toolkit
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi Winston,

Read my previous post on backwards compatibility. At the end of the day, that's the reason they *could not* change Java to behave the way you want it to. There are other examples of Java doing less than ideal things so as not to break between versions. If they didn't have to worry about backwards compatibility they might have considered it, but keeping JVMs functioning is a core part of upgrading Java.
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Scott Selikoff wrote:At the end of the day, that's the reason they *could not* change Java to behave the way you want it to. There are other examples of Java doing less than ideal things so as not to break between versions. If they didn't have to worry about backwards compatibility they might have considered it, but keeping JVMs functioning is a core part of upgrading Java.

True, but the case where it would break is so bizarre (checking two numeric wrapper objects for instance equality) that I think I might have risked it. But I'm not Sun.

And there is precedence for it. Also in 1.5, BigDecimal.toString() was changed to return either a basic number or standard form (eg, "1.2e5"), depending on scale. Have a look at the toPlainString() docs and you'll see a note about it.

As I say, just barking at the moon. But it's fun sometimes.

Winston
 
Master Rancher
Posts: 2045
75
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Winston Gutkowski wrote:(...)As I say, just barking at the moon. But it's fun sometimes.

Hmmm, neighbors usually throw a vase to that barking dog...

Where I do find it confusing is when you have a List<Integer>, and then you call 'list.remove(i)'.
Unless you know the JLS specification by heart (I don't), the question is: does autoboxing take place
here?
 
Scott Selikoff
author
Bartender
Posts: 4093
21
Eclipse IDE Flex Google Web Toolkit
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Winston Gutkowski wrote:True, but the case where it would break is so bizarre (checking two numeric wrapper objects for instance equality) that I think I might have risked it. But I'm not Sun.


Actually you probably wouldn't have risked it, even if you were Sun. The fact is, there are billions of lines of Java code spanning endless numbers of libraries and JARs. if you write server side code, you probably rely on dozens, if not hundreds, of JARs. If even one of those JARs contains a single line of == with two wrappers, the behavior will change and the entire platform can fall apart. Also, keep in mind that some code is generated, and would be just as susceptible to the problem.

In short, unless you want to risk throwing away decades of code, you're probably not going to break backwards compatibility.
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Piet Souris wrote:Where I do find it confusing is when you have a List<Integer>, and then you call 'list.remove(i)'.
Unless you know the JLS specification by heart (I don't), the question is: does autoboxing take place here?

Yes, followed by a widening reference conversion (IntegerObject). And then that is compared using
  i == null ? value == null : i.equals(value)
Same with contains(i).

Sheesh, it was all so much simpler before...

Winston
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Scott Selikoff wrote:Actually you probably wouldn't have risked it, even if you were Sun. The fact is, there are billions of lines of Java code spanning endless numbers of libraries and JARs. if you write server side code, you probably rely on dozens, if not hundreds, of JARs. If even one of those JARs contains a single line of == with two wrappers, the behavior will change and the entire platform can fall apart.

Then how do they justify the change to BigDecimal? Surely the same rule applies...indeed, I'd say it's more likely to break code since toString() is used a lot - often in cases where it isn't obvious (who hasn't written n + ""?).
Autoboxing involved changing the JLS anyway, so why not just document the change to '=='?

However, it's far too late to change now. And anyway, it'd be boring if everything was as we expect it to be.

Winston
 
Roel De Nijs
Sheriff
Posts: 11338
177
AngularJS Chrome Eclipse IDE Hibernate Java jQuery MySQL Database Spring Tomcat Server
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Winston Gutkowski wrote:
Piet Souris wrote:Where I do find it confusing is when you have a List<Integer>, and then you call 'list.remove(i)'.
Unless you know the JLS specification by heart (I don't), the question is: does autoboxing take place here?

Yes, followed by a widening reference conversion (IntegerObject).

That's incorrect!

When you use the remove(int) method, there is no autoboxing taking place! And the reason for this, is quite simple and already mentioned a bunch of times in this thread: backwards compatibility! So I honestly don't see why you should know the JLS specification by heart. In Java 1.4, this method will remove the element at the given index (as there was no autoboxing). And this behavior didn't change in Java 5.0 and later versions; otherwise all previous written code would be broken (and would need lots of TLC). So if you want to invoke the remove(Object) method you have to use a reference variable. A code snippet to illustrateOutput:
before: [10, 20, 30, 40]
after remove(int): [10, 20, 40]
after remove(Object): [20, 40]


Hope it helps!
Kind regards,
Roel
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Roel De Nijs wrote:That's incorrect!

Oops. Forgot it was overloaded. You're quite right.

My apologies, Piet. I was thinking of Map.remove(), where that IS what happens.

Winston
 
Piet Souris
Master Rancher
Posts: 2045
75
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Winston Gutkowski wrote:My apologies, Piet. I was thinking of Map.remove(), where that IS what happens.

Winston

It's okay, Winston, I knew it for years.
Point is: if you look at the List API, both are possible here.
Backward compatibility says me nothing, since I wasn't even born
at the time of Java 1.4 (well, eh, as a matter of speaking!)

I noticed it a couple of years ago, when I thought it would remove
value I, but instead it removed index i. Bad luck that value was a
legal index. It took me quite a while to find my error, though.
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Piet Souris wrote:It's okay, Winston, I knew it for years.

Yeah, I thought you probably did, Socrates.

I'd love to give you a cow, but I've run out for today. Can't let an error go unacknowledged though.

Winston
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
So: do we have any consensus on my (admittedly recently amended) title?

My take on "autoboxing" is a bit like my verdict on generics:
It's what Sun thought they could get away with without violating the "absolute" barrier of backward-compatibility and, as such, is a "kludge".

For something that offers the power and type-safety that generics does, I can live with it.

For a piece of syntactic sugar that basically saves you some typing (autoboxing), I think I could have waited for a more comprehensive solution; and the mere fact that we get so many posts about it says to me that is isn't "complete" - ie, it violates either Liskov or POLA, though I'm not exactly sure which and where.

Winston
 
Piet Souris
Master Rancher
Posts: 2045
75
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I'm not much of a theoretical guy. From a practical PoV: I find it a blessing, as
long as they maintain the split in primitives and objects.

Winston Gutkowski wrote:I's love to give you a cow (...)

Don't bother. You've given me plenty of these and I have now more
than I can handle.

Thanks to Devaka (@Devaka: thank you!) I'm finally in a position where
I can return something, for all your fine writings (some of which I still
totally disagree!). Enjoy the pie.

Greetings,
Piet
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Piet Souris wrote:Enjoy the pie.

Cheers mate.

Winston
 
Don't get me started about those stupid light bulbs.
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!