Why are these == but not `equals()`?

China☆狼群 提交于 2019-11-27 07:50:59

The reason for

X == y

being true has to do with binary numeric promotion. When at least one operand to the equality operator is convertible to a numeric type, the numeric equality operator is used. First, the first operand is unboxed. Then, both operands are converted to int.

While

X.equals(y)

is a normal function call. As has been mentioned, y will be autoboxed to a Short object. Integer.equals always returns false if the argument is not an Integer instance. This can be easily seen by inspecting the implementation.

One could argue that this is a design flaw.

(small) Integer instances are cached, so the invariant x == y is holded for small instances (actually -127 +128, depends on JVM):

Integer a = 10;
Integer b = 10;

assert(a == b); // ok, same instance reused

a = 1024;
b = 1024;

assert(a == b); // fail, not the same instance....
assert(a.equals(b)); // but same _value_

EDIT

4) and 5) yield false because equals check types: X is an Integer whereas Y is a Short. This is the java.lang.Integer#equals method:

public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }

    return false;
}

The morale of the story:

Autoboxing/unboxing is confusing, as is type promotion. Together, they make for good riddles but horrendous code.

In practice, it seldom makes sense to use numeric types smaller than int, and I'm almost inclined to configure my eclipse compiler to flag all autoboxing and -unboxing as an error.

Your problem here is not only how it treats == but autoboxing... When you compare Y and 9 you are comparing two primitives that are equal, in the last two cases you get false simply because that's how equals work. Two objects are equal only if they are of the same kind and have the same value. When you say in "X.equals(y)" you are telling it to do Integer.equals(Short) and looking at the implementation of Integer.equals() it will fail:

   public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
    }

Because of autoboxing the last two will result in the same failure as they will both be passed in as Shorts.

Edit: Forgot one thing... In the case of results.add(X == y); it will unbox X and do (X.intValue() == y) which happens to be true as well as 9 == 9

This automatic conversion is called autoboxing.

Ricky

I remember a good practice for overriding "equal(object obj)" is of first checking the type of the parameter passed in. So perhap this causes X.equals(Y) to be false. You might check the souce code to dig out the truth :)

A bit more detail on how autoboxing works and how "small" valued Integer objects are cached:

When a primitive int is autoboxed into an Integer, the compiler does that by replacing the code with a call to Integer.valueOf(...). So, the following:

Integer a = 10;

is replaced by the compiler with the following:

Integer a = Integer.valueOf(10);

The valueOf(...) method of class Integer maintains a cache that contains Integer objects for all values between -127 and 128. If you call valueOf(...) with a value that's in this range, the method returns a pre-existing object from the cache. If the value is outside the range, it returns a new Integer object initialized with the specified value. (If you want to know exactly how it works, lookup the file src.zip in your JDK installation directory, and look for the source code of class java.lang.Integer in it.)

Now, if you do this:

Integer a = 10;
Integer b = 10;
System.out.println(a == b);

you'll see that true is printed - but not because a and b have the same value, but because a and b are referring to the same Integer object, the object from the cache returned by Integer.valueOf(...).

If you change the values:

Integer a = 200;
Integer b = 200;
System.out.println(a == b);

then false is printed, because 200 is outside the range of the cache, and so a and b refer to two distinct Integer objects.

It's unfortunate that == is used for object equality for value types such as the wrapper classes and String in Java - it's counter-intuitive.

Java will convert an Integer into an int automatically, if needed. Same applies to Short. This feature is called autoboxing and autounboxing. You can read about it here.

It means that when you run the code:

int a = 5;
Integer b = a;
System.out.println(a == b);

Java converts it into:

int a = 5;
Integer b = new Integer(a);
System.out.println(a == b.valueOf());
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!