hashCode()
and equals(Object)
Did you ever see a project where classes break the contract between
the methods hashCode()
and equals(Object)
defined in
java.lang.Object
? This contract says, that equal objects must
provide the same hash-code. The criteria can be changed in any custom class by
overriding the default implementations in java.lang.Object
.
Objects
breaking the contract can not be used in a Java-Standard-Collection
(package java.util
), because the collection will behave unpredictably.
You may find this problem in a legacy project, where you are
unable (because of risks or workload) to change any
implementation of the hashCode()
and equals(Object)
methods.
Another problem with the contract and Java-Standard-Collections can occur if
you need - in a specal use case for example - other criteria for equality than
implemented. In such a case, you would need to extend or wrap the values, so that
the Java-Standard-Collection will use your use case specific implementations of
hashCode()
and equals(Object)
.
Generelly spoken, HE-Collections do a transparent correction for incorrect
hashCode()
and equals(Object)
implementations. That is,
for classes that brake the contract, HE-Collections use a standard correction
for these types - if available. Standard corrections can be realised by implenting an
interface of the HE-Collections. If a special correction is needed, HE-Collections
can be configured via the use of constructor-parameters.
In a legacy project you may need standard corrections as described above.
To achive this, any type that needs a standard correction, must implement the
HE-Collection interface EqualsAndHashCorrection
. This interface defines
the methods hashCodeInHeCollection()
and equalsInHeCollection(Object)
,
that serve as correction for the incorrect implemented methods
hashCode()
and equals(Object)
.
You use a HE-Collection by creatin an instance as follows:
java.util.Set<Type> mySet = new HeHashSet<Type>();
What you get is a set that you can use just as any other Java-Standard-Set.
This set - however - may contain "original" (no wrong implementation of the contract)
and corrected objects at the same time.
The corrections, HE-Collections provide, are independent from generics and transparent to
the user. A HeArrayList
for example provides the standard methods
add(T value)
and T get(int index)
from java.util.List
.
You can even add the content of a java.util.Collection
into the
HeArrayList
. But doing the vice versa is risky, because if the HE-Collection
contains objects the break the contract, the Java-Standard-Collection that gets them
may behave in an undefined way.
The corrections in the HE-Collections are implemented as Switch
. A switch
implements the methods hashCode()
and equals(Object)
and delegates calls to them forward to the "corrections" (other methods). Per default a HE-Collection contains to
switches in this order:
EqualsAndHashCorrection
.
hashCode()
and
equals(Object)
(no correction).
Every switch provides a type. A switch can be used for any object, that is an instance of that type. A HE-Collection holds a list of switches. The first switch found, that is applicable for a given object, will be used.
If you want special corrections, you can "extend" the standard-switch-list by adding your
own switches before the standard switches. Or you can start creating your own switch-list
from scratch. For more details see in the java-docs AbstractSwitch
and
AbstractWrapperFactory
.
Example:
public class SpecialCorrection
extends AbstractSwitch<BrokenType>
{
@Override
protected Class<BrokenType> getType()
{
return BrokenType.class;
}
@Override
protected int getCorrectHashCode(BrokenType element)
{
...
}
@Override
protected boolean getCorrectEqualsValue(
BrokenType element,
Object other)
{
...
}
}
public class SpecialFactory extends AbstractWrapperFactory
{
public WrapperFactory()
{
super();
add(new BrokenTypeSwitch());
}
}
java.util.Set<BrokenType>mySet =
new HeHashSet<BrokenType>();
Remark the possibilities you have with HE-Collections! Even if a base-type breaks the contract, but not all sub-types (overriding the wrong implementations), you can use a HE-Collection that is generically typed to the base-type!
Frank Lemke