1

Assume we have the following class:

public class SingleElementRefType
{

    protected JAXBElement<SequenceType> sequence;

    // ...
}

It contains the sequence field of type JAXBElement.

JAXBElement is a third-party class (standard API, actually), which is in essence a pure value class, but it for some reason does not implement hashCode and equals methods.

From my point of view, these methods are absolutely reasonable there.

I would like to implement equals and hashCode methods for SingleElementRefType as well as SequenceType so that I could do a deep comparison of this values. But JAXBElement stands in the way.

Since I can't extend JAXBElement, my idea was to integrate hashCode and equals into the aggregating class (SingleElementRefType here):

JAXBElement<SequenceType> theSequence;
theSequence = this.getSequence();

final QName theSequenceName = theSequence.getName();
currentHashCode = ((currentHashCode* 37) +
    ((theSequenceName!= null)?theSequenceName.hashCode(): 0));

final Object theSequenceValue = theSequence.getValue();
currentHashCode = ((currentHashCode* 37) +
    ((theSequenceValue!= null)?theSequenceValue.hashCode(): 0));

But then I had second thoughts if I'm not breaking some convention or rule here.

Are there any dangers of implementing hashCode and equals for third-party classes in my aggregating classes?

Update: for certain reasons my code may not have further runtime dependencies. So I can't use Guava or commons-lang here.

lexicore
  • 42,748
  • 17
  • 132
  • 221
  • It is a perfectly reasonable thing to do if you need it; if you use Guava, you may consider using an `Equivalence`, see [here](http://javachannel.org/posts/guavas-equivalence-strategy-for-equalshashcode/). – fge Nov 29 '14 at 16:09
  • @fge Can't use Guava here - my code must not have further runtime dependencies. But a good tip, I'll check the concept. – lexicore Nov 29 '14 at 16:12
  • Couldn't the containing class (I assume that this would be one that represents another XML element) take care of equals and hashCode simply by bypassing the JAXBElement? I do know that xjc doesn't include these methods, but a plugin could, and so could a hand-written class where an element needs the JAXBElement wrapper. – laune Nov 30 '14 at 15:18
  • @laune We're writing an XJC plugin right now. That's where the question come from. We could generate code for `JAXBElement`s but won't we break anything with it? I think not, but better ask... – lexicore Nov 30 '14 at 15:45
  • Then do not include the tag name in the equals and hashCode computations. It's pointless. If the containing class's equals is being called, and we've bypassed the is-it-the-same-class stage we have objects of the same type, and then the name must be equal, so it's not necessary to consider. – laune Nov 30 '14 at 15:53
  • Assume that there is an equals and a hashCode in JAXBElement, and both consider all attributes of that class. Would you call this equals for a "deep comparison"? What if their QName namespaces differ, everything else being equal? Do you intend to compare XML documents or Java object hierarchies? – laune Nov 30 '14 at 16:23
  • @laune *"then the name must be equal"* - why so? You can get different names in `@XmlElementRef` property. – lexicore Nov 30 '14 at 16:32
  • @laune *"Would you call this equals for a "deep comparison"?"* - yes. *"What if their QName namespaces differ, everything else being equal?"* - then they're different. – lexicore Nov 30 '14 at 16:33
  • Your last "yes" in your last comment indicates that you want to compare XML documents and not just object contents. – laune Nov 30 '14 at 16:58
  • "different names in @XmlElementRef" Indeed. This would make two references to equal Java objects that happen to be in differently tagged XML elements not equal?!? – laune Nov 30 '14 at 17:22
  • @laune Please join the discussion on GitHub: https://github.com/highsource/jaxb2-basics/issues/11 – lexicore Nov 30 '14 at 17:56

1 Answers1

0

It is perfectly reasonable to override the hashCode and equals method of your model class. While overriding them, java specifies a list of contracts to be followed.

For equals,

  • reflexive [x.equals(x) = true]
  • symmetric[x.equals(y)=true, then (y.equals(x)==true) ]
  • transitive [x.equals(y)==true && y.equals(z)==true, then x.equals(z)==true]
  • consistent
  • ‘non-nullity’ [x.equals(null)==false]

For hashCode,

  • consistency : return same integer for the same object
  • if (o1.equals(o2), then o1.hashcode == o2.hashcode)
  • not viceversa

This answer can provide you insight into best practices to be followed while overriding hashCode().

And for overriding equals follow these practices :

  • Use == to check if the argument is a reference to this object
  • Use instanceof operator to check if the argument has the correct type
  • Cast the argument to the correct type
  • For each “significant” field in the class, check if that field of the argument matches the corresponding field of this object

A way you can make sure that your overridden hashCode and equals method are effectively following the contracts will be to test them using Junits. Need of overriding hashCode and equals will be more evident when, at some point of time, have to use your model class in a hash based collection, say a hashmap or hashset.

Community
  • 1
  • 1
djames
  • 366
  • 5
  • 17