2

The Java method System.identityHashCode(...) is returning a different value for an object when I call it internally using the this reference, compared to calling it on a variable reference of the same object.

class MyObject {
public void test(MyObject that){
        LOGGER.info("this hash: {}",System.identityHashCode(this));
        LOGGER.info("that hash: {}",System.identityHashCode(that));
        LOGGER.info("equals: {}",this == that);
    }
}

and the test...

MyObject o = new MyObject();
o.test(o);

and the output...

this hash: 263009111
that hash: 524075148
equals: false

What would cause this to happen? The real object in question is a Hibernate entity, but I've added the above test directly to the code and it's shown the same behavior in a specific scenario. Why would an object show a different identity hash code using the this keyword than it would on a reference to itself? I've also confirmed that the reference is correct by setting some fields of the object and confirming that the local fields were set to the same values. So if the reference is correct, why does identityHashCode(...) return two different values, and why does the "==" operator fail? I was under the impression that this method was specifically implemented to identify references to the same logical object?

The Shoe Shiner
  • 697
  • 4
  • 20
  • 3
    "Why would an object show a different identity hash code using the this keyword than it would on a reference to itself" <- But it isn't the same object otherwise (this==that) would return true. I cannot reproduce the behaviour and it would probably be good if you could provide a [MVCE](https://stackoverflow.com/help/mcve). – OH GOD SPIDERS May 09 '17 at 15:47
  • It is the same object via reference. I can set fields of the object like *this.myField = * and then test *that.myField == this.myField* and the result is true. Unfortunately I can only reproduce this in the context of code that I don't own, so I can't provide a MCVE. Right now my hunch that is hibernate is doing something to the entity that breaks the identity/this contract, but I can't figure out what that is or why. – The Shoe Shiner May 09 '17 at 15:50
  • 1
    Maybe this http://stackoverflow.com/questions/25340606/what-does-the-hibernate-proxy-object-contain adds some insight (Hibernate creates proxy objects for entity objects to support lazy loading) – Gyro Gearless May 09 '17 at 15:57
  • @DanWatson The fact that changing the fields of one object has an effect of the fields of another one isn't necessarily proof that both are the same object per reference. One could simple be a wrapper class around the other: For example if you create an `ArrayList a` and then a `List b = Collections.synchronizedList(a)` any modification on b will also modify a. But they are not the same object! – OH GOD SPIDERS May 09 '17 at 16:01
  • I think the hibernate comment is more on the right track. The field being set is not a complex object. it's a primitive field, and it's set to the exact same value in both places, but only by a single assignment. I think what's happening is that hibernates javassist magic is messing with the *this* operator, such that it returns a different identity from a variable reference. – The Shoe Shiner May 09 '17 at 16:04
  • I've just created the MyObject class with your code (adding the LOGGER instance) and execute your test, and the result is: `[main] INFO stuff.MyObject - this hash: 640070680 [main] INFO stuff.MyObject - that hash: 640070680 [main] INFO stuff.MyObject - equals: true` – Spock May 09 '17 at 16:07
  • Unfortunately it's not reproducible in general, the code was just a guide. It's a hibernate entity, and its not code that I own. I'm going to try and step through the specific scenario with a server debugger and really see if the objects are different. – The Shoe Shiner May 09 '17 at 16:13

1 Answers1

2

They are two separate objects (even if they may conceptually contain the same data) as is demonstrated by the fact that == is false.

System.identityHashCode()

Returns the same hash code for the given object as would be returned by the default method hashCode(), whether or not the given object's class overrides hashCode(). The hash code for the null reference is zero.

In other words a standard hashCode that uses just the address of the object. Every different object will give a different value. This is nothing to do with hibernate, it's how System.identityHashCode() is designed to work.

If you want the behaviour you expect then use the equals and hashCode method on the objects hibernate has returned to you.

You most likely have some sort of tree structure going on:

this->A<-that

Making changes to A whether through this or that is visible through either reference but they are still two different java objects even though they are wrapping the same inner values. This is most likely hibernate proxy objects used to support things like lazy loading of values.

To confirm this for yourself step through it in a debugger and look at the actual objects.

Tim B
  • 40,716
  • 16
  • 83
  • 128
  • If they are two separate objects then why does setting an internal field of one (by reference not by method) *always* set the internal field of the other to the exact same value? I think they are the same object and hibernate is doing some magic, as suggested in one of the other comments. – The Shoe Shiner May 09 '17 at 16:00
  • It's not a tree structure. It's a primitive field, and both fields are being set with a single assignment. I think this proves they are the same object? – The Shoe Shiner May 09 '17 at 16:02
  • 1
    @DanWatson Your own test proves they are not the same object. `equals: false`. You _think_ they are the same object but clearly they are not. I'm not sure how the field magic is happening but without seeing the code (and I don't have time to dig that deep anyway) all I can say is that if `==` returns false then the identity hashcode would normally be different and that is expected. So your question as asked is answered. An interesting follow on question here is how modifying the internal field of two different objects affects them both. I expect hibernate reflection magic is involved. – Tim B May 09 '17 at 16:05
  • @TimB: "An interesting follow on question here is how modifying the internal field of two different objects affects them both. I expect hibernate reflection magic is involved." <- If they are entities and still attached to a persitence context (aka synchronized live with the database) this would explain that behaviour pretty easily i guess. – OH GOD SPIDERS May 09 '17 at 16:23
  • 1
    I confirmed in a debugger that the objects are indeed separate. i.e. there is an instance of *MyObject* and an instance of *MyObject_$$_javassist_* which is the proxy object. So that explains the IdentityHashCode difference. I'm still not certain how the proxy is swapping the *this* reference out with a new object, but it appears that that's what it's doing. – The Shoe Shiner May 09 '17 at 16:37
  • "uses just the address of the object" Well, now, the hash code is not an address, so this statement is false. – Lew Bloch May 09 '17 at 17:09
  • @lewbloch http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#hashCode() "This is typically implemented by converting the internal address of the object into an integer". I simplified for brevity. – Tim B May 09 '17 at 17:14
  • "Converting" from an address is not _being_ an address, and "typically implemented" is not "always" or "required to be" implemented. Just because some implementations might base the number on an address that later changes every GC cycle doesn't mean that the number _is_ an address. – Lew Bloch May 09 '17 at 17:17
  • @lewbloch feel free to edit and expand on that point if the simplification offends you. It isn't really relevant to the question being asked though so a simplified version seemed sufficient – Tim B May 09 '17 at 17:20
  • "Offends"? It is not about me. It's about the truth. A "simplification" that is not true is not a simplification of the truth but of a falsehood. – Lew Bloch May 09 '17 at 18:02
  • @LewBloch Like electrons orbit the nucleus like a little solar system, DNA looks like a spiral ladder, etc? It may not be "true" but it's still true enough to explain what he is seeing without going into a long and tedious explanation that he is quite capable of looking up for himself if interested in. – Tim B May 09 '17 at 18:22