Summary
Pojomatic 2.0 has been released, and uses InvokeDynamic to give performance nearing that of hand-rolled code.
Advertisement
Announcing Pojomatic 1.0
Version 2.0 of Pojomatic has
been released. Pojomatic provides an easy way to implement
the equals(Object), hashCode()
and toString() methods inherited
from java.lang.Object.
This release targets Java 7 or higher. The reason for this is to
take advantage of the new InvokeDynamic instruction introduced in
Java 7. While Pojomatic 1.0 used reflection to do it's work, this
new version generates byte code using ASM, and uses InvokeDynamic to be able
to access potentially private members of pojomated classes. The
result is that the performance of equals and hashCode is now close
to that of what would be seen with hand-rolled code. (To learn more about how this is done, come see my JavaOne talk this year).
Starting in Java 7, reflection no longer will necessarily return a
class's elements in any particular order. Consequently, this release
also uses ASM to read in the class file for a pojomated class, in order
to determine the original order of fields and methods on the
class.
Overview of Pojomatic
For those not familiar with Pojomatic, the typical work needed for most classes is:
import org.pojomatic.Pojomatic;
import org.pojomatic.annotations.AutoProperty;
@AutoProperty // tells Pojomatic to use all non-static fields.
public class Pojo {
// Fields, getters and setters...
@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); }
}
While this is adequate more most cases, there are numerous ways to
customize how Pojomatic will build up the implementations of
the equals, toString
and hashCode methods. The
@AutoProperty
annotation can instruct Pojomatic to use fields or getters to accesses
properties. Alternatively, one can annotate individual fields and/or
accessor methods with the
@Property
annotation to include them explicitely, or to exclude certain
properties if @AutoProperty is being used. For any
property, a
@PojomaticPolicy
can be set to indicate which methods the property should be included
in. By default, a property is used
for each of the equals, hashCode
and toString methods, but any combination is possible,
subject to the restriction that if a property is used
for hashCode, then it must be used
for equals as well.
Proper implementation of equals for class hierarchies
As discussed in
How to
Write an Equality Method in Java, challenges arise in
satisfying the contract for equals when class hierarchies
come into play. The solution suggested in that article is to
introduce an additional method, canEqual, which the
implementation of equals will use to ensure that
instances of a parent class do not accidentally declare themselves to
be equal to an instance of a subclass which has
redefined equals. If all the classes in a hierarchy use
Pojomatic, this step is not necessary; Pojomatic keeps track of
whether instances of two related classes can be equal to each other or
not via the
areCompatibleForEquals
method. Two classes are compatible for
equality if they each have the same set of properties designated for
use with the equals method. If a subclass has a need to
implement the equals method without using Pojomatic, it
can be annotated with
@OverridesEquals
to indicate that it is not compatible for equality with it's parent class.
Comparing two Pojomated instances
A common use of the equals method is to facilitate the use
of the assertEquals methods of JUnit or TestNG.
When assertEquals fails, the exception method includes
the toString representation of each instance. One pain point which no
doubt will be familiar to many is that of trying to determine why two
instances are not equal when they have a large number of properties.
Often, the only option is to copy the failure message into an editor
which allows comparing the toString representations to look for
differences. Pojomatic helps address this by adding
method, Pojomatic.diff
which can reveal the differences between two instances of a Pojomated
class.
The PojomaticTestUtils
library leverages this capability to provide
an assertEqualsWithDiff method which will call out the
differences between two instances in the failure messaged in the event
that they are not equal.
Getting Pojomatic
Pojomatic is available on the central maven repository; if you use
maven, it is just a dependency away: