Reputation: 3992
Consider we got a class MyEntity
which has some field with getters and setters. Furthermore the classes EntityA
and EntityB
extend MyEntity
. There are some fields in MyEntityA
that are not in MyEntityB
and vice versa. As we are talking about entities, EntityA
and EntityB
do have their own equals
-methods looking like
@Override
public boolean equals(Object other) {
if ((this == other))
return true;
if ((other == null))
return false;
if (!(other instanceof EntityA))
return false;
EntityA castOther = (EntityA) other;
return ((this.getId() == castOther.getId()) || (this.getId() != null && castOther.getId() != null && this.getId().equals(castOther.getId())));
}
This equals
-method is neded for hibernate to identify the unique entity.
Now I want to compare an instance of EntityA
with an instance of EntityB
. I define them to be identical, if all the fields from MyEntity
match.
To check this I let eclipse generate the equals method for me and copy it to a method like isEqualWithRegardsToContent(MyEntity other)
.
I see one big problem with this approach:
If someone ever adds a new column to either one of the entities and doesn't update the isEqualWithRegardsToContent(MyEntity other)
-method, it get's buggy: The entities might be considered as equal with regards to content, although they aren't.
I don't see that a unit-test would help here.
Do you have any best practices?
Upvotes: 2
Views: 4548
Reputation: 14348
Say, you have equals()
method in superclass that compares common properties.
In subclass equals()
you can first call super.equals()
, then, if compared object is also of this class, compare only specific properties. So in EntityA
you write:
@Override
public boolean equals(Object o) {
boolean eq = super.equals(o);
if (eq && o instanceof EntityA) {
EntityA e = (EntityA) o;
return Objects.equals(this.propOne, e.propOne)
&& Objects.equals(this.propTwo, e.propTwo)
&& // compare other properties
} else
return eq;
}
Such, objects of the same concrete class will be compared by full set of properties, including common and specific properties, and instances of different classes will be compared only by common properties. Though this is non-standard way which violates transitive property of the contract, it may solve your particular problem.
Upvotes: 2
Reputation: 149025
If I correctly understand your problem:
MyEntity
lacking a getId
fieldEntityA
and EntityB
both defining a id
field but form different generatorsEntityA
(resp. EntityB
) they compare equal if and only if their id fields have same value - I assume that in that case all fields inherited from MyEntity
have same valueEntityA
object with a EntityB
one, they should compare equal if and only if all the fields inherited from MyEntity
are equal.Here is what you could do:
equals
method in MyEntity
that returns true if and only if all fields compare equal - if you cannot use the equals
method for that, name the method ABequals
(for example)declare the equal method in subclass EntityA
(resp. EntityB
)
@Override
public boolean equals(Object other) {
if ((this == other))
return true;
if ((other == null))
return false;
if (!(other instanceof EntityA))
return MyEntity.equals(other); // or return ABequals(other);
EntityA castOther = (EntityA) other;
return ((this.getId() == castOther.getId()) || (this.getId() != null && castOther.getId() != null && this.getId().equals(castOther.getId())));
}
In that case you must implement a hash method in the MyEntity
class that uses all the fields and you should not overide it in subclasses. That way you ensure that two objects that compare equal will have same hash value - the above definition already guaranties that the equals
function is an equivalence relation.
Upvotes: 0
Reputation: 3502
A class' equals()
method should only work for other instances of that class. If you want to compare EntityA
and EntityB
with equals()
, then your equals()
method should be defined on MyEntity
(along with hashCode()
) and both EntityA
and EntityB
should not have equals()
methods themselves. Otherwise, you are very likely to run afoul of the contract for equals()
. Libraries such as Hibernate depend upon equals()
properly adhering to the contract.
Upvotes: 0
Reputation: 24192
generally speaking its impossible to have a fully functioning equals when dealing with inheritance trees. superclass.equals(subclass)
will not return the same result as subclass.equals(superclass)
, breaking symmetry (which is the basic contract for equal - if a.eq(b)
then also b.eq(a)
).
you could implement equals() only at the top level, thereby comparing the entire hierarchy from the point of view of the superclass. this will work, but will not compare by "all fields" (only those in the superclass). commonly entities may be compared by just their primary keys
personally i dont usually mix the 2 - equals()
and hashcode()
i reserve for simple data-storage classes (or keys used to lookup in maps) that are not polymorphic.
see a details coverage here - http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals-2.html
Upvotes: 2