Wednesday, 15 August 2012

Pojomatic for Equals(), HashCode() and toString()

From the website itself: "Pojomatic provides configurable implementations of the equals(Object), hashCode() and toString() methods inherited from java.lang.Object"

This is best described with an example (available on Github, in the Introduction-To-Pojomatic directory):
@AutoProperty
public class Item {

    @Property(policy=PojomaticPolicy.EQUALS_TO_STRING)
    private long id = 0;

    private String description = "";

    @Property(policy=PojomaticPolicy.NONE)
    private transient int transientData;

    @Override
    public boolean equals(Object o) {
        return Pojomatic.equals(this, o);
    }

    @Override
    public int hashCode() {
        return Pojomatic.hashCode(this);
    }

    @Override
    public String toString() { 
        return Pojomatic.toString(this);
    }

    // Constructors, Setters & Getters...

}
Notice that equals(), hashCode() and toString() are overriden to call Pojomatic.

The @AutoProperty annotation enables Pojomatic on an class. It has two optional properties:
  • The auto detect policy indicates whether FIELDs or METHODs should be searched for properties to take into account. The search can also be disabled with NONE. By default, the FIELDs are searched for.
  • The default Pojomatic policy which indicates whether the properties are going to be included in equals(), hashCode() and toString(). By default, the policy is set to ALL. Other values are: EQUALS, EQUALS_TO_STRING, HASHCODE_EQUALS, NONE, TO_STRING.
The class level configuration can also be overridden at the field (or method) level with the @Property annotation. It also has two properties:
  • The name, which identifies the property in toString().
  • The policy, which is very similar to the default Pojomatic policy, but at the field/method level. It can take the same values, plus one: DEFAULT, which means 'the class level policy' (or ALL if none is defined).
The following:
Item item = new Item();
item.setDescription("Blue furniture");
item.setId(123456);
item.setTransientData(9999);

System.out.println("equals   : " + item.equals(new Object()));
System.out.println("hashCode : " + item.hashCode());
System.out.println("toString : " + item.toString());
Generates the following output:
equals   : false
hashCode : -2089463829
toString : Item{id: {123456}, description: {Blue furniture}}
Pojomatic also offers:
  • a feature to control formatting in toString()
  • a mean to prevent a subclass from overridding equals()
  • Assuming B extends A, but you don't want an instance of B to be equal to an instance of A ever, there is a mean to notify Pojomatic
  • Assuming B extends A, and you would like to retrieve the difference in terms of properties used to compute equals(), Pojomatic.diff(...) provides this information
  • One can retrieve the Pojomator used to compute equals(), hashCode() and toString(); it also contains an interesting isCompatibleForEquality() method to check whether another class can be used in equals()