Wednesday, January 09, 2008

Experience using apache commons EqualsBuilder class

Following are my learnings on how to use the EqualsBuilder class in apache commons lang library.

C:\Work\EqualsProto\src\equalsproto\Main.java


package equalsproto;

//~--- non-JDK imports --------------------------------------------------------

import java.util.Arrays;
import org.apache.commons.lang.builder.EqualsBuilder;


class A
{
private String s = "Watsh";
private int x = 10;
private float y = 20.2f;

//~--- constructors --------------------------------------------------------

A() {}

A(int i, int i0, String string)
{
this.x = i;
this.y = i0;
this.s = string;
}

//~--- methods -------------------------------------------------------------

/**
* Method description
*
*
* @param obj

*
* @return
*/
@Override
public boolean equals(Object obj)
{
if (obj instanceof A == false) {
return false;
}

if (this == obj) {
return true;
}

A rhs = (A) obj;

/** Note:

* Do not use appendSuper when the super class is java.lang.Object as

* default implementation of equals in Object class will return true only

* when two references are pointing to the same object instance and hence

* the effect is not desirable.
*/
return new EqualsBuilder().append(s, rhs.s).append(x,
rhs.x).append(y, rhs.y).isEquals();
}
}



class B
{
private String z = "Rajneesh";
private A a;
private A[] array;

//~--- constructors --------------------------------------------------------

B(String z, A a, A[] array)
{
this.z = z;
this.a = a;
this.array = array;
}

@Override
/**
* Learning:
* 1. appendSuper() should not be used as it then calls super.equals()

* for java.lang.Object class which will return true only when both lhs and

* rhs references point to the same object instance and hence will return false

* when the 2 object instances are different but meaningfully equivalent.

*
* 2. To compare arrays, you will either need to use the

* EqualsBuilder.reflectionEquals() approach or if you are using the

* EqualsBuilder.append() approach then append(array1, array2) calls

* array1.equals(array2) which will only do a shallow comparison for the

* 2 arrays involved. So in such a case, you must use Arrays.deepEquals() for

* all array members of your class and once that equality is met you can use

* EqualsBuilder.append() for rest of the non-array instances.

*
* NOTE: I have not tested for how this approach works for Collection classes.

*/
/*public boolean equals(Object obj)
{
if (obj instanceof A == false) {
return false;
}

if (this == obj) {
return true;
}

B rhs = (B) obj;

return new EqualsBuilder().append(z, rhs.z).append(a,

rhs.a).append(array, rhs.array).isEquals();
}*/
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
}



/** Testing the use of EqualsBuilder.
*
* @author wrajnees

*/
public class Main
{

/**
* @param args the command line arguments

*/
public static void main(String[] args)
{
A a1 = new A(2, 43, "xxx");
A a2 = new A(2, 43, "xxx");
if (a1.equals(a2)) {
p("a1 equals a2");
} else {
p("a1 not equals a2");
}

A[] array1 = new A[2];
A[] array2 = new A[2];

array1[0] = a1;
array1[1] = new A();

array2[0] = a2;
array2[1] = new A();

// comparing arrays

if (Arrays.deepEquals(array1, array2)) {
//if (array1.equals(a2)) { // -- does not work
p("arrays are equal");
} else {
p("arrays arent equal");
}

// comparing more complex object with containment and array

B b1 = new B("Test", a1, array1);
B b2 = new B("Test", a2, array2);
if (b1.equals(b2)) {
p("b1 equals b2");
} else {
p("b1 not equals b2");
}
}

private static void p(String s) {
System.out.println(s);
}
}



1 comment:

Neill Laney said...

My commons-lang EqualsBuilder didn't work until I overrode and implemented HashBuilder.toHashCode. Don't know if that was assumed knowledge, but thanks for the post!