Guava: Equality helper class API

Created on 19 Sep 2017  路  4Comments  路  Source: google/guava

We've got an Equality helper class in our code base that helps us implement equals and hashCode methods, and I think it'll be a nice feature for guava. It's similar to ComparisonChain, but for equality.

The API is basically this:

@Override
public boolean equals(Object obj) {
    return Equality.startComparison(this, obj, MyObj.class)
            .superEquals(super::equals)
            .field(MyObj::val1)
            .field(MyObj::val2)
            .intField(o -> o._int)
            .booleanField(o -> o._bool)
            .arrayField(o -> o._array)
            .field(o -> o._obj, new CustomEquivalence())
            .result();
}

@Override
public int hashCode() {
    return Equality.startHash(this)
            .superHashCode(super::hashCode)
            .field(MyObj::val1)
            .field(MyObj::val2)
            .intField(o -> o._int)
            .booleanField(o -> o._bool)
            .arrayField(o -> o._array)
            .field(o -> o._obj, new CustomEquivalence())
            .result();
}

public boolean staticEquals(T obj1, T obj2) {
    return Equality.startComparison(obj1, obj2)
        .field(...)
        .field(...)
        .result();
}

The benefits of this API are:

  • It automatically handles null checks, casting, object equality, type safety, and short-circuits when appropriate.
  • Implementing equals and hashCode are symmetric - just copy and paste the .field(...) calls. It's also easy to see if you've missed out a field from one of the methods
  • It removes all the boilerplate otherwise necessary, and you can easily see what is used to determine equality in the class.

There is AutoValue, but there are many situations where you can't or don't want to use auto-generated classes like that, and this can be used to easily replace existing equals methods without otherwise changing the class.

package=base status=will-not-fix type=addition

Most helpful comment

If you're going to do anything like this, I'd tend to want to see it the other way around: better factory methods for creating an Equivalence, and then putting that Equivalence as a private static constant in the class and delegating to it for both the equals and hashCode implementations, so you can't accidentally look at one field for equals and forget it in hashCode or vice versa.

Better Equivalence factory methods, like Comparator's new factory methods that got massively improved in Java 8, aren't a bad idea.

All 4 comments

Interesting API, but as you mention, our preferred solution is AutoValue. Updating a class to use @AutoValue can often be done w/o touching any existing callers.

I'll leave this request open for now to see how others feel, but generally I think we'd like to avoid having to write these implementations by hand.

There are some cases where you either can't or don't want to use AutoValue, and then this API will really help with those manual implementations.

This API is also very useful for easily writing an Equivalence implementation.

If you're going to do anything like this, I'd tend to want to see it the other way around: better factory methods for creating an Equivalence, and then putting that Equivalence as a private static constant in the class and delegating to it for both the equals and hashCode implementations, so you can't accidentally look at one field for equals and forget it in hashCode or vice versa.

Better Equivalence factory methods, like Comparator's new factory methods that got massively improved in Java 8, aren't a bad idea.

I tend to agree with lowasser. However, the number of custom Equivalence implementations in the Google codebase is 1/47th that of Comparator. At this point I'm not sure this investment of effort would be high value.

Was this page helpful?
0 / 5 - 0 ratings