EqualsBuilder: an API for object comparison in Java
What is EqualsBuilder?
Compare objects in Java using the following DSL:
@Override
public boolean equals(Object o) {
return EqualsBuilder.test(this, o)
.comparing(Human::getName)
.comparing(Human::getSurname)
.comparing(Human::getAge)
.areEqual();
}
Motivation
In Java there are three methods inherited from Object
class that are often overloaded:
java boolean equals(Object o)
int hashCode()
String toString()
The most simple implementation for toString
, generated by an IDE might look as follows:
@Override
public String toString() {
return "Address{" +
"house=" + house +
", street='" + street + '\'' +
", city='" + city + '\'' +
'}';
}
This code is not exactly what is considered 'pretty' by most developers. A more appealing implementation, using a helper from Guava would look like:
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("house", house)
.add("street", street)
.add("city", city)
.toString();
}
With a better readability and maintainability, it is usually favored by the developers more.
For a .hashCode()
method overloads in Java 7 an Objects.hash(Object...)
method was introduced, which allowed developers to write the following code:
@Override
public int hashCode() {
return Objects.hash(house, street, city);
}
At the same time Objects.equals(a, b)
was also introduced, making .equals overloads somewhat more readable.
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Address that = (Address) o;
return Objects.equals(house, that.house) &&
Objects.equals(street, that.street) &&
Objects.equals(city, that.city);
}
Still, this implementation has some nasty boilerplate code like null checks, type checks, and type casts.
As an alternative one might consider using EqualsBuilder
class from Apache Commons library:
@Override
public boolean equals(Object o) {
if (o == null) { return false; }
if (o == this) { return true; }
if (o.getClass() != getClass()) {
return false;
}
Address rhs = (Address) o;
return new EqualsBuilder()
.appendSuper(super.equals(o))
.append(house, rhs.house)
.append(street, rhs.street)
.append(city, rhs.city)
.isEquals();
}
Unfortunately, not only does this implementation not help with casts and type checks, but it also takes more space on your screen and attempts to fix English language by introducing a method named "isEquals".
To compliment already existing helpers for overloading .hashCode
and .toString
, an attempt was made to create a DSL for comparing two Java objects.
Goals
The goals for this project are:
- Make a simple yet good DSL for comparing objects by different fields
- Reduce boilerplate code required to use this DSL to zero
- Reduce text repetition (
.append(street, other.street)
might look like.append(street)
) - Make it comparable to a naive implementation generated by an IDE performance-wise