Archive for October, 2007
EnhancerByCGLIB and equals()
by on Oct.16, 2007, under Uncategorized
Suppose you have a simple pojo class called MyPojo. Containing some arbitrary attributes and an id and version. The by IntelliJ generated equals() method for this object will look as follows.
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass())
return false;
RecordImpl record = (RecordImpl) o;
if (version != record.version) return false;
if (id != null ? !id.equals(record.id) : record.id != null)
return false;
return true;
}
Suppose now we use Hibernate to persist this object and there are two related object through which we retrieve two lazy instances of this object. The nature of Hibernate will now enforce both instances to be bytecode enhanced by cglib, which results in two instances of the class MyPojo$$EnhancerByCGLIB$$.
So what happens now if we call equals on those instances (let’s call them p1 and p2). So p1.equals(p2) results in the following execution lines.
if (this == o) return true;if (o == null || getClass() != o.getClass())if (o == null || !(o instanceof MyPojo))MyPojo pojo = (MyPojo) o;if (version != pojo.version) return false;if (version != pojo.getVersion()) return false;if (id != null ? !id.equals(pojo.id) : pojo.id != null)if (id != null ? !id.equals(pojo.getId()) : pojo.getId() != null)return true;MyPojo pojo = (MyPojo) o;
Which is false, so we go on
Here we run into a problem. The class of o is MyPojo$$EnhancerByCGLIB$$ which does not equals the class of this, i.e.MyPojo. So what we need to do to fix this is to compare the class of p2 through different means
Ouch. Hold on. Calling the version attribute will give an exception. Remember that the class is cglib enhanced. So the properties still have to be retrieved. We have to use the get method.
Again. We have to use the getter.
And finally we know they are equal.
I wish this would be it. However, if MyPojo would have an interface in front of, the next problem comes around. CGLib will enhance the interface instead of the implementation. As a concequence the instance can no longer be casted to the implementing class. Hence, line 3 will give a ClassCastException. We have to adjust it to work with the interface. Fortunately we no longer use direct property access, so this won’t be a problem.
So now we have a equals which should evaluate correctly two instances. But I don’t like it. Making the equals method dependent on a implementing interface restricts me from implementing multiple interfaces. Or else I would have make the equals method more complex. If anyone has a better sollution, please let me know.