Comparing primitive to wrapper object with == behaviour unexplained
Asked Answered
M

4

27

I have a piece of code which I need to understand:

public static void main(String[] args) {
    Character c = new Character('a');
    Character cy = new Character('a');
    char cx = 'a';

    System.out.println(c == cx);
    System.out.println(cx == cy);
    System.out.println(c == cy);
}

Output:

true
true
false

I am unable to understand why only the third statement is failing.

EDIT: This question is different to the .equals vs == question as this about primitive versus object comparison.

Manslaughter answered 11/4, 2016 at 9:2 Comment(6)
not quite, char primitive to object matching is working whereas the object to object matching is not...so i wondered whyManslaughter
Why is new Object() == new Object() false?Bactericide
"This question is different to the .equals vs == question as this about primitive versus object comparison." No, it really isn't, since understanding that difference naturally explains this one. == does reference comparison; once you recognize that, the answer is trivial.Astaire
See also #2832445Wildfowl
@Astaire Except that that's wrong. c is a reference but c == cx doesn't do a reference comparison. The question is not the same.Trophozoite
@Trophozoite The question is why c == cy returns false, not why c == cx returns true. Granted the OP may be confused on the latter point, but that could have been researched or asked if they applied the knowledge about reference comparison to c == cy, instead of wrongly assuming that the other comparisons implied it should be true.Astaire
S
45

c and cy refer to different instances of the Character class (each time you invoke a constructor, you create a new instance), so comparing these references returns false.

On the other hand, when you compare either of them to the primitive cx, they are unboxed to char, and the char comparison returns true.

Had you used Character.valueOf('a') instead of new Character('a'), you would have gotten the same instance in both calls, and the reference comparison would have returned true (since valueOf returns a cached Character instance if the argument <= 127).

Semimonthly answered 11/4, 2016 at 9:3 Comment(4)
This part of Java is very confusing (not only) for beginners unfortunately. Since Java 5 there is autoboxing, which tries to be helpful but makes the behaviour of == even for tricky. Before autoboxing, I believe the compiler would have rejected the first two comparisons.Wehrle
Note that Character a = Character.valueOf('a') can be shortened to Character a = 'a' and that even valueOf will return instances that are not equal for non-ASCII characters.Wehrle
Good IDEs can be configured to give a warning when boxing and unboxing will occur, as it does here.Scraggy
@Wehrle I don't see any auto-boxing the question, only auto-unboxing.Someday
C
25
 System.out.println(c == cx);
 System.out.println(cx == cy);

Since one is primitive and another is a wrapper class of it, unboxing happens and primitive comparison takes place (==).

Whereas:

 System.out.println(c == cy);

is an Object comparison. Different instances are getting compared so == won't work in this case.

Cimbura answered 11/4, 2016 at 9:4 Comment(0)
S
-1

Charcter class is not singleton, so always a new object will be creating when calling the contructor and new objects refer to their respective references.So (c == cy) gives you false

Semanteme answered 11/4, 2016 at 9:12 Comment(7)
why do the other two comparisons give us true, then?Wehrle
@Wehrle for other comparison(involving primitive) boxing and autoboxing will happenSemanteme
Even for Singletons each invocation of the constructor creates a new instance. It's just that invoking the constructor is made harder.Kisangani
@Kisangani In singleton constructor is called onceSemanteme
Yes, that is what the pattern is trying to achieve. Still, their constructor is not special in any way (except that it has the private-modifier), and if you invoke the constructor a second time, you get a separate instance.Kisangani
@Kisangani the only way would be using reflection... and that's off-topicDelaminate
@Delaminate as is Singleton - it has nothing to do with the question.Kisangani
D
-2

it's obvious why the last comparision gives false: both Characters are explicitly initialized with new, thus being different objects

why the first two comparisions give true, however, is only partly clear: the char value is definitely used to retrieve a pre-stored Character instance, but I don't know how exactly the pre-defined Character objects are mapped to this pre-stored instance

I would expect, though, it works like "=="-comparision for String objects: if at compile time one of the compared instances is a pre-stored Character then the compiler inserts a call to equals() replacing the "=="-comparision

Dragrope answered 11/4, 2016 at 11:40 Comment(3)
It's the opposite (as others have already correctly explained): 15.21.1. Numerical Equality Operators == and !=: 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). Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8). So what's happening is unboxing, not boxing.Kisangani
The char value is not at all ("definitely") used to retrieve a Character instance - instead the other side of the == is converted to a primitive character. And the compiler never replaces == with equals. Not for String objects, not for Character objects, not for any type of object.Declinatory
But how come then that for Strings, == behaves just like equals()? It can't be array unboxing, that would do ref compare again, and from all I know operators can't be carried over as natives...Dragrope

© 2022 - 2024 — McMap. All rights reserved.