Pattern Mixin in Java 8 for implementing equals, hashCode, toString and compareTo

1 Reference

-The Mixin pattern > https://en.wikipedia.org/wiki/Mixin

-Minborg's blog article > New Java 8 "Object Support Mixin Pattern" for overriding Object.equals(), hashCode(), toString() and compareTo()

-Full working source code, including running examples > Attached at the end of this article

2 Introduction

As common as needing to implement, at least, hashCode and equals is making a mistake doing it: either because our IDE generated code isn't generic enough or because of a human error.

Let's see a way to automate and industrialize the implementation of equals, hashCode, toString and, optionally, compareTo using Java 8 and applying the Mixin pattern for maximum code reuse.

Although the ideas and source code presented here is mainly based in Minborg's blog, implementation partially differs significantly.

3 See it in action: let's "include" equals, hashCode, toString and, optionally, compareTo methods

Two examples will allow you to quickly begin taking advantage of this pattern:

·Example '3a' shows, step by step, how easy is to add these four methods to a class that isn't yet extending another one (by inheritance).

·Example '3b' shows the same mechanism for a class that it is already extending another one (by implementing an interface).

Both examples are based the Person class shown below, where all three attributes must be taken into account for implementing hashCode and equals methods:

public class Person{
private final String name;
private final String email;
private final int    born;
public Person(String name, String email, int born) {
    this.name = name;
    this.email = email;
    this.born = born;
}
public String getName() {
    return name;
}
public String getEmail() {
    return email;
}
public int getBorn() {
    return born;
}
}

3a For a class that can extend another one

In this case, the easiest way is to extend from our support class 'ReflectionObjectSupport':

-Annotate the attributes to take into account for hashCode and equals with @EqualsAndHashCode

-Override implementation of method compareToMembers to indicate which attributes to take into account by the compareTo method

-See implementation of class 'PersonCan.java' and test it with 'MainCan.java'

Result:

public class PersonCan extends ReflectionObjectSupport<PersonCan> {
private final String name;
private final String email;
private final int    born;
public PersonCan(String name, String email, int born) {
    this.name = name;
    this.email = email;
    this.born = born;
}
@EqualsAndHashCode
public String getName() {
    return name;
}
@EqualsAndHashCode
public String getEmail() {
    return email;
}
@EqualsAndHashCode
public int getBorn() {
    return born;
}
@Override
public Comparable<?>[] compareToMembers() {
    return mkComparableArray(getName());
}
}

3b For a class that cannot extend another one

Since Java can only extend from one class, in this case our class will implement the interfaces 'ReflectionObjectMixin' and 'Comparable':

-Annotate the attributes to take into account for hashCode and equals with @EqualsAndHashCode

-Override implementation of methods equals, hashCode, toString, compareTo and compareToMembers to indicate which attributes to take into account by the compareTo method

-See implementation of class 'PersonCannot.java' and test it with 'MainCannot.java'

Result:

public class PersonCannot extends Object implements Comparable<PersonCannot>, ReflectionObjectMixin<PersonCannot> {
private final String name;
private final String email;
private final int    born;
public PersonCannot(String name, String email, int born) {
    this.name = name;
    this.email = email;
    this.born = born;
}
@EqualsAndHashCode
public String getName() {
    return name;
}
@EqualsAndHashCode
public String getEmail() {
    return email;
}
@EqualsAndHashCode
public int getBorn() {
    return born;
}
@Override
public int compareTo(PersonCannot o) {
    return _compareTo(o);
}
// More than one attribute can be indicated, eg: mkComparableArray(getName(), getBorn())
@Override
public Comparable<?>[] compareToMembers() {
    return mkComparableArray(getName());
}
@Override
public boolean equals(Object obj) {
    return _equals(obj);
}
@Override
public int hashCode() {
    return _hashCode();
}
@Override
public String toString() {
    return _toString();
}
}