EqualsHash
For an actual type parameter Person
this factory generates a class PersonWithEqualsHash that
extends class Person. PersonWithEqualsHash
has a public equals() and a public hashCode() method
that can be used to test two PersonWithEqualsHash
objects for equality and calculate a hash code for a PersonWithEqualsHash
object, respectively.
In equals(), the corresponding primitive attributes of the
caller and the actual parameter are compared directly and the class type
attributes are compared using their respective equals() method.
This is possible, because the Java root class Object declares
a public method equals(), so that all objects are guaranteed
to have one (unlike clone(), for example).
Analogously, the generated hashCode() reads the values of the
primitive attributes directly, and calls hashCode() on the
class type attributes, which are all guaranteed to declare hashCode().
The resulting values are combined to a single one using bitwise
exclusive-or.
Note that the factory works only for actual parameters that do not
declare private attributes. The factory would generate code that would
try to access these private attributes, and because Java forbids
subclasses to access the private attributes of its superclasses and the
generated class is a subclass of the actual type parameter this could
not be compiled.
Factory/Java Source
The source can be found in the Factory/Java .zip-archive in factory/classes/examples/EqualsHash.factory:
<param> <var> T </var> </param>
package test;
public class
<apply>
<apply>
<class> factory.Toolbox
</class>
<method> getRelativeName
</method>
<args>
<var> T
</var>
</args>
</apply>
<method> concat </method>
<args>
<const> "WithEqualsHash"
</const>
</args>
</apply>
extends <var> T </var>
{
public boolean equals(Object o) {
// classes have to be equal
if(!o.getClass().equals(this.getClass()))
return
false;
<var> T </var> a =
(<var> T </var>) o;
// iterate over all attributes of
T
<for> <var> I
</var>
<apply>
<class>
factory.Toolbox </class>
<method>
getAllFields </method>
<args>
<var> T </var> </args>
</apply>
<body>
<if>
<apply>
<apply>
<var> I </var> <method>
getType </method>
</apply>
<method> isPrimitive </method>
</apply>
<then>
if(
this.
<apply>
<var> I </var>
<method> getName </method>
</apply>
!=
a.
<apply>
<var> I </var>
<method> getName </method>
</apply>
)
return false;
</then>
<else>
if( this.
<apply>
<var> I </var>
<method> getName </method>
</apply>
.equals(
a.
<apply>
<var> I </var> <method> getName </method>
</apply>
) == false
)
return false;
</else>
</if>
</body> </for>
return true;
}
// requirement: a.equals(b) =>
a.hashCode()==b.hashCode()
public int hashCode() {
int code = 0;
// iterate over all attributes of
T
<for> <var> I
</var>
<apply>
<class>
factory.Toolbox </class>
<method>
getAllFields </method>
<args>
<var> T </var> </args>
</apply>
<body>
<if>
<apply>
<apply>
<var> I </var> <method>
getType </method>
</apply>
<method> isPrimitive </method>
</apply>
<then>
code ^=
(int)
this.
<apply>
<var> I </var>
<method> getName
</method>
</apply>
;
</then>
<else>
code ^=
this.
<apply>
<var> I </var>
<method> getName
</method>
</apply>
.hashCode();
</else>
</if>
</body> </for>
return code;
}
}
Test Program
The following unparameterized factory
can be found in factory/src/test/EqualsHashTest.factory in the
Factory/Java .zip-archive. It applies factory EqualsHash
to class Person, instantiates three
objects a, b and c of the resulting class PersonWithEqualsHash
and sets their attributes to arbitrary values. The values for b
and c are the same, so that they have the same hash code and a
call to equals() on them returns true.
package test;
public class EqualsHashTest {
public static void main(String argv[]) {
<let> <var> T
</var>
<apply>
<factory> examples/EqualsHash </factory>
<args>
<const> test.Person </const> </args>
</apply>
<body>
<var> T
</var> a = new <var> T </var>();
a.name =
"Mr.X";
a.plz = 4711;
a.id = 1;
<var> T
</var> b = new <var> T </var>();
b.name =
"Mr.Y";
b.plz = 4712;
b.id = 2;
<var> T
</var> c = new <var> T </var>();
c.name =
"Mr.Y";
c.plz = 4712;
c.id = 2;
System.out.println("a.hashCode()=="+a.hashCode());
System.out.println("b.hashCode()=="+b.hashCode());
System.out.println("c.hashCode()=="+c.hashCode());
System.out.println("a.equals(b)=="+a.equals(b));
System.out.println("a.equals(c)=="+a.equals(c));
System.out.println("b.equals(c)=="+b.equals(c));
</body> </let>
}
}
This factory can be compiled in the factory directory with
java -classpath classes factory.Factory -javad src -classd classes src/test/EqualsHashTest
and run with
java -classpath classes test.EqualsHashTest
Execution of the class yields the following textual output:
a.hashCode()==2400265
b.hashCode()==2400282
c.hashCode()==2400282
a.equals(b)==false
a.equals(c)==false
b.equals(c)==true