3

The Java System.identityHashCode(...)method 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?

7
  • 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. Commented May 9, 2017 at 15:47
  • It is the same object via reference. I can set fields of the object like this.myField = <uniquevalue> 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. Commented May 9, 2017 at 15:50
  • 2
    Maybe this stackoverflow.com/questions/25340606/… adds some insight (Hibernate creates proxy objects for entity objects to support lazy loading) Commented May 9, 2017 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! Commented May 9, 2017 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. Commented May 9, 2017 at 16:04

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

11 Comments

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.
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?
@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.
@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.
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.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.