Reusable equals and hashCode implementation

I am working on a project where many classes require the right typical implementations of equals and hashCode : each class has a set of finite fields initialized when building with "deep" immutable objects ( null are intended to be accepted in some cases) that will be used for hashing and comparison.

To reduce the amount of template code, I thought about writing an abstract class that provides common implementations of this behavior.

 public abstract class AbstractHashable { /** List of fields used for comparison. */ private final Object[] fields; /** Precomputed hash. */ private final int hash; /** * Constructor to be invoked by subclasses. * @param fields list of fields used for comparison between objects of this * class, they must be in constant number for each class */ protected AbstractHashable(Object... fields) { this.fields = fields; hash = 31 * getClass().hashCode() + Objects.hash(fields); } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj == null || !getClass().equals(obj.getClass())) { return false; } AbstractHashable other = (AbstractHashable) obj; if (fields.length != other.fields.length) { throw new UnsupportedOperationException( "objects of same class must have the same number of fields"); } for (int i=0; i<fields.length; i++) { if (!fields[i].equals(other.fields[i])) { return false; } } return true; } @Override public int hashCode() { return hash; } } 

It is intended to be used as follows:

 public class SomeObject extends AbstractHashable { // both Foo and Bar have no mutable state private final Foo foo; private final Bar bar; public SomeObject(Foo foo, Bar bar) { super(foo, bar); this.foo = Objects.requireNonNull(foo); this.bar = bar; // null supported } // other methods, no equals or hashCode needed } 

Basically this offer is here with some differences.

This seems like a simple but good approach to reducing verbosity and still has efficient implementations of equals and hashCode . However, since I don’t remember ever seeing something like this (with the exception of the answer mentioned above), I would like to specifically ask if there is any point against this approach (or perhaps some improvement that could be applied) before applying it throughout the project.

+5
source share
4 answers

I already see two problems with this approach:

  • Two objects will be considered equal if and only if all their fields coincide. This does not always happen in the real world.
  • By creating extend classes from AbstractHashable , you can no longer extend from any other classes. This is a pretty high price for reusing equals and hashCode .

The first problem can be solved by passing more metadata to your AbstractHashable class, which allows you to determine which fields are optional. For example, you can pass another array to AbstractHashTable , which contains index positions that will be ignored as their elements through the setter. The second question can be solved using composition. Instead of expanding AbstractHashTable reorganize it so that it can establish a HAS-A connection with its users, and not with the IS-A relationship.

However, since I do not remember ever seeing something like this (with the exception of the answer above), I would like to specifically ask if there is any point against this approach.

This approach will definitely affect the readability aspect of your code. If you can come up with a more readable approach (say using annotations), I see nothing wrong with wanting to reuse equals and hashCode .

What everyone says, modern IDEs like eclipse can easily generate an equals and hashCode for you, so do you really need to come up with a solution of this kind? I believe not.

+5
source

Based on my experience, this seems bad, as you will increase the error you may get at runtime and compile time (remember that all using these objects in lists, etc. can now give you unprocessed behavior). What if the order of the fields in the classes is different? Secondly, do you abuse inheritance? Maybee selects some frameworks ( https://projectlombok.org/ ) that generate a hash code and are equal based on the annotation?

0
source

The solution should work.

However, it is a bit weak in terms of OOP:

  • You duplicate data (fields in a superclass and a field in an object), so your object is twice as large; also, if you ever need to make them volatile, you need to maintain state in two places.
  • class hierarchy is enforced, only to provide equals / hashCode

Modern IDEs generate such methods quite easily, if you want to simplify these methods, you can also use frameworks (e.g. Lombok) or libraries (such as Guava Objects / Java 8 Objects).

0
source

I would suggest looking at apache HashCodeBuilder and EqualsBuilder . It has reflection based methods such as reflection HashCode and reflectionEquals.

0
source

Source: https://habr.com/ru/post/1263312/


All Articles