If that is a requirement--and I think we'd all agree that it is--then why allow it to be otherwise? Require that those two methods are delegated to an inner class that forces them to be consistent.
This is the kind of thing I have in mind...
The first component is a class called Equable which implements both equals and hashCode. There is a constructor which takes an Iterable of objects, which correspond to the fields of a user type. Here's a link to the source on github.
BaseEquable is an abstract class which can be extended by a user type and which defines both equals and hashCode in terms of an abstract method getEquable which returns, for a sub-type, an instance of Equable. Source link.
A user type which extends BaseEquable simply has to implement getEquable something like this (where, in this example, there are two fields: x and y):
@Override | |
public Equable getEquable() { | |
Collection<Object> elements = new ArrayList<>(); | |
elements.add(x); | |
elements.add(y); | |
return new Equable(elements); | |
} |
Now, because the actual work of equals and hashCode is delegated to methods which enforce consistency, there is no danger of those two methods being inconsistent. I've also demonstrated (in the same repository of github) that it's easy to extend to including a consistent version of compareTo also.
OK, back to work!