2019.04.20 Implementing .equals() and .hashCode() in Java Easier method. - GlenKPeterson/One-off_Examples GitHub Wiki

Implementing .equals() and .hashCode() for database classes can be as simple as this:

@Override
@SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
public boolean equals(Object other) {
    return equalsIdOrFields(this, other,
                            (that) -> Objects.equals(field1, that.field1) &&
                                      Objects.equals(field2, that.field2));
}

@Override
public int hashCode() {
    return hashIdOrFields(id, field1, field2);
}

First you need an interface for classes that have ID's. I call it "ID'd" or:

interface Idd {
    public long getId();
}

Add methods like the following in the Utils class in your project and import them where you implement .equals() and .hashCode()

/**
 * Given `this` and `other` from your equals method, checks referential equality,
 * successful cast, and comparison by ID before running through any additional tests you pass in.
 */
public static <T extends  Idd> boolean equalsIdOrFields(
        @NotNull T orig,
        @Nullable Object other,
        @NotNull Predicate<T> tests) {

    if (orig == other) {
        return true;
    }

    @SuppressWarnings("unchecked")
    Class<T> clazz = (Class<T>) orig.getClass();
    if ( !clazz.isInstance(other) ) {
        return false;
    }

    // Now it's safe to cast.
    @SuppressWarnings("unchecked")
    final T that = (T) other;

    // If both objects have surrogate keys assigned,
    // just compare them and be done.
    if ( (orig.getId() != 0) && (that.getId() != 0) ) {
        return (orig.getId() == that.getId());
    }

    // Now do class-specific tests
    return tests.test(that);
}

/**
 * Given the surrogate key and important fields, returns an appropriate hashCode.
 */
public static int hashIdOrFields(long id, Object... fields) {
    if (id == 0) {
        return Arrays.hashCode(fields);
    }
    // return (possibly truncated) surrogate key
    return (int) id;
}

For a better explanation of what's going on here, see: My previous post