hashCode()/equals() using lambdas

Writing proper hashCode() and equals() methods is really important, but a pretty boring task. Luckily IDEs nowadays generate these, so we don’t have to worry about them too much. But this approach has two downsides:

  1. it adds at least 20 lines of complex looking code to often very simple classes (e.g. DTOs or JPA entities), which reduces readability and might have a negative impact on static code analysis
  2. the IDEs take different approaches, so the code looks different for e.g. Eclipse and IntelliJ

Lambdas as an alternative

To simplify the process of generating hashCode() and equals() methods I thought about using Java 8 lambdas. This is the approach I came up with:

public class HashCodeEqualsBuilder {

    public static  int buildHashCode(T t, Function<? super T, ?>... functions) {
        final int prime = 31;
        int result = 1;
        for (Function<? super T, ?> f : functions) {
            Object o = f.apply(t);
            result = prime * result + ((o == null) ? 0 : o.hashCode());
        }
        return result;
    }

    public static  boolean isEqual(T t, Object obj, Function<? super T, ?>... functions) {
        if (t == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (t.getClass() != obj.getClass()) {
            return false;
        }
        T other = (T) obj;
        for (Function<? super T, ?> f : functions) {
            Object own = f.apply(t);
            Object foreign = f.apply(other);
            if (own == null) {
                if (foreign != null) {
                    return false;
                }
            } else if (!own.equals(foreign)) {
                return false;
            }
        }
        return true;
    }
}

To use it in a class (e.g. DTO) type the following:

@Override
public int hashCode() {
    return HashCodeEqualsBuilder.buildHashCode(this, MyClass::getA, MyClass::getB);
}

@Override
public boolean equals(Object obj) {
    return HashCodeEqualsBuilder.isEqual(this, obj, MyClass::getA, MyClass::getB);
}

Based on some microbenchmarks I did, the performance is equal to the autogenerated implementation (e.g. by Eclipse). I will start using this from now on and can recommend using this approach for two reasons:

  1. it does not add additional lines of code to your project that might pop up as uncovered by unit tests
  2. updating the implementation (if ever) must only happen at a single place
Copyright © christophbrill.de, 2002-2018.