Field comparison using reflection

Do anybody know if there is a class or library that allows somebody to compare two similar objects' fields using reflection. I can't override the .equals() method for many of the classes I'm working with, so I'd like a dynamic way of going in and comparing two objects' fields.I'm looking for something that would compare fields of any type (primitive, Object, Collection, etc.)

Maybe something like this:

Class A {

String a;

String b;

int c;

Vector d;

public A(String ina, String inb, int inc, Vector ind) {

this.a = ina;

this.b = inb;

this.c = inc;

this.d = ind;

}

public static void main(String[] args) {

A anObject = new A("a", "b", 1, new Vector());

A anotherObject = new A("a", "b", 2, new Vector());

boolean b = reflectCompare(anObject, anotherObject);

}

boolean reflectCompare(anObject, anotherObject);

}

}

[978 byte] By [javidjam] at [2007-9-26 4:53:00]
# 1

If the objects are the same class you just need to write your own Comparator class that extracts the values from the classes (by field, or by public methods), and then compares them. Then to compare them you would just do something like:

Comparator myComparator = new MyComparator();

if( myComparator.compare( objA, objB ) == 0 )

return true;

else

return false;

Equals usually isn't the best thing to use for comparing objects. Either implement Comparable (if you can) or just implement a Comparator. It is much more flexible than just overriding equals. If you have a Comparator, then you can sort collections of objects, etc.

Collections.sort( myArrayList, myComparator );

gweedo at 2007-6-29 18:45:35 > top of Java-index,Core,Core APIs...
# 2

I had a related problem, the solution of which will probably solve yours too.

I frequently need a toString() method for objects to use during debugging, but I do not want to spend a lot of time hand writing all the code. (I hate IDEs for most purposes -- my dev environment is TextPad on top of the latest JDK, and my main form of debugging is good ole System.out.println...)

It turns out that all you need to do is write a generic class that uses reflection to get all the field names and values, and then for any class it takes just one line of code to implement a toString method which prints out the complete object's state. I share that code below.

For your purpose, which is seeing if two objects are equal, you may want to adapt my code below to see if:

(a) the 2 objects have completely matching field names

(b) for each corresponding field name, the values are equal

You very well may want to place this code in a Comparator class, as one of the other responders suggested.

Note that the main tricky thing in the code below is that you need to turn off security checks against private members and such; the rest is really straightforward. The code is completely javadoced and self-tested, which makes it 3 times longer than what you may need, but I wrote it for my personal library where I actually put an emphasis on quality. Hmm, looks like this webform does not like long lines; hope it does not do something idiotic like introduce hrad line breaks...

Happy computing,

-brent

// Copyright (c) 2001 Brent Boyer. All Rights Reserved.

/*

Programmer Notes:

-- for quick use during debugging, when want to minimize typing,

may want to make a shadow class of this class which has really abbreviated names

(e.g. "S" for the class name, "gs" for getState, etc)

another thing that would be cool during debugging is if you could pop up

a JFrame with the state data inside!

-- could add Field metadata (e.g. a string of all the Modifiers for the field)

-- may also be interested in all the (non-static-final?) fields from superclasses too?

(i.e. need a method called getCompleteState)

In this case, need an algorithm that recursively goes up the inheritance tree to find fields

-- The contract of the getDeclaredFields method says nothing about the order in which the Fields

will be returned, but in fact the order seems to be the declaration order in the source code.

But if I wanted to print out the fields in alphabetical order, would need to make a call to

Arrays.sort()

with a Comparator that used the field's names

*/

package bb.util;

import java.lang.reflect.*;

/**

* This class analyses the state of any Object.

* Specificly, it uses reflection to query an Object about all it's Fields and their values.

* <p>

* Since the purpose of the toString method in most classes is simply to return a String

* representing the Object's state, <i>this class makes it extremely trivial to write

* a toString method</i>. In particular,

* <blockquote>

* <code>

*public String toString() throws IllegalArgumentException, SecurityException {

*return StateAnalyser.getState(this);

*}

* </code>

* </blockquote>

* is all most classes will need to write.

* <p>

* There are a few limitations with the above quick toString implementation.

* First, see the warnings in the getState method.

* Second, the above implementation will be slower than a custom written toString implementation.

* (This is rarely a concern, as toString is usually used for diagnostics, but if speed is a factor,

* then you will have to custom write toString.)

* <p>

* Unless otherwise noted, all the methods of this class are multithread safe.

* <p>

* <b>Warning</b>: the above statement is true only with respect to the operation

* of this class itself. A major caveat must be given for any argument

* that is a mutable Object. Here, the possibility exists that another Thread

* (besides the Thread calling our method) could be simultaneously modifying the Object.

* Unpredictable results might occur.

* <p>

* @see #getState

* @author Brent Boyer

*/

public class StateAnalyser {

// -- constants --

/**

* A guesstimate for the number of chars that will be needed to describe

* the state of a typical Object.

*/

protected static final int TYPICAL_STATE_CHAR_SIZE = 256;

// -- constructor --

/** This private constructor suppresses the default (public) constructor, ensuring non-instantiability. */

private StateAnalyser() {}

// -- getState --

/**

* This method returns a String which describes the target Object (classname and hashcode)

* and all of it's declared Fields (names and values).

* (A declared field is one that is declared in the source code of a class -- it does not include

* Fields defined in any superclasses.)

* Additionally, it prints out the current Thread which is executing this method.

* <p>

* In order to get the values of otherwise inaccessible Fields (e.g. private ones),

* this method will call <code>setAccessible(true)</code> on every Field.

* When this method is thru with a given Field, however, it will restore the original

* accessibility setting.

* <p>

* <b>Caveats and Warnings</b>:

* <ul>

* <li>

*<i>the formatting of the String returned by this method was designed for

*Fields of primitive or String type</i>; if the Field is an Object type which

*implements toString and returns a multi-line result, it may not look great

* </li>

* <li>

*this method will not work at all if there are security policies against reflecting

*on the target Object and/or getting it's Field names and values

* </li>

* <li>

*<i>there is the danger that infinite callbacks could occur</i> (e.g. if a Field is of a type

*that implements toString, and if that type's toString method calls back this method);

*even if that does not happen, you could still generate an enormous result

*for sufficiently complicated object graphs

* </li>

* </ul>

* <p>

* @throws IllegalArgumentException if arg target is null

* @throws SecurityException if a SecurityManager forbids some action (e.g. accessing the Fields, or calling a Field's setAccessible method)

*/

public static final String getState(Object target) throws IllegalArgumentException, SecurityException {

if (target == null) throw new IllegalArgumentException("arg target is null");

StringBuffer sb = new StringBuffer(TYPICAL_STATE_CHAR_SIZE);

appendObjectInfo(target, sb);

sb.append( " [\n" );

appendFieldsInfo( target.getClass().getDeclaredFields(), target, sb );

sb.append( "]" );

appendThreadInfo(sb);

return sb.toString();

}

protected static final void appendObjectInfo(Object target, StringBuffer sb) {

sb.append( target.getClass().getName() )

.append( " (hashcode: " )

.append( target.hashCode() )

.append( ")" );

}

protected static final void appendFieldsInfo(Field[] fields, Object target, StringBuffer sb) {

for (int i = 0; i < fields.length; i++) {

boolean originalAccessibility = fields.isAccessible();

fields.setAccessible(true);// force accessibility for the duration of this method

sb.append( "\t" )

.append( fields.getName() )

.append( ": " )

.append( extractFieldValue(fields, target) )

.append( "\n" );

fields.setAccessible(originalAccessibility);// restore original accessibility

}

}

protected static final Object extractFieldValue(Field field, Object target) {

try {

return field.get(target);

}

catch (Throwable t) {

return handleGetProblem(t);

}

}

/**

* Returns a useful diagnostic message. (See the description in the javadocs

* for the get method of Field for where I got the descriptions.)

* <p>

* This method was written genericly, for any invocation of the get method.

* However, in this class, other than an ExceptionInInitializerError,

* the other Exceptions in the code below should never happen if the appendFieldsInfo

* method works as expected.

*/

protected static final String handleGetProblem(Throwable t) {

if (t instanceof IllegalAccessException)

return "<problem: argument is inaccessible>";

else if (t instanceof IllegalArgumentException)

return "<problem: it appears that the Object supplied to the Field's get method is not an instance of the class or interface corresponding to the Field>";

else if (t instanceof NullPointerException)

return "<problem: the Object supplied to the Field's get method is null, but the Field is an instance member>";

else if (t instanceof ExceptionInInitializerError)

return "<problem: the initialization provoked by the get method failed>";

else

return "<the following unexpected problem happened: " + ThrowableUtil.getTypeAndMessage(t) + ">";

}

protected static final void appendThreadInfo(StringBuffer sb) {

sb.append( "\n" )

.append( "{Current Thread: " )

.append( Thread.currentThread().toString() )

.append( "}" );

}

// -- Test (inner class) --

/**

* An inner class that consists solely of test code for the parent class.

* <p>

* Putting all the test code in an inner class rather than a <code>main</code>

* method of the parent class has the following benefits:

* <ul>

* <li>the test code is cleanly separated from the working code</li>

* <li>any <code>main</code> in the parent class is now reserved for a true program entry point</li>

* <li>all the test code may be easily excluded from the final shippable product

*by removing all the Test class files

*(e.g. on Windoze, delete all files that end with <i>$Test.class</i>)</li>

* </ul>

*/

public static class Test {

private static class ReflectionTest {

private static final String SOME_STRING = "yabba dabba do";

private static boolean someBoolean = true;

private int someInt = 3;

}

public static void main(String[] args) throws Exception {

System.out.println( StateAnalyser.getState( new ReflectionTest() ) );

}

}

}

bbatman at 2007-6-29 18:45:35 > top of Java-index,Core,Core APIs...
# 3

> If the objects are the same class you just need to

> write your own Comparator class that extracts the

> values from the classes (by field, or by public

> methods), and then compares them.

I appreciate your response.

My question is more geared towards the actual comparison strategy. Let me be more specific.

I have an object called BaseBusinessObject (BBO) from which I derive many classes. BBO has a .equals() method that compares a few of its own fields. The classes that derive from BBO are not allowed to override .equals() for architecture reasons. So now I want to compare objects that derive from BBO. I have created a class (as you have suggested) that will take two BBO objects (or derived BBO objects). I can grab all the fields and get all the values now.

The only problem is that it becomes increasingly more complicated when the fields are Objects or Collections themselves. If they are merely primitives I can just compare them for value. But if the field is a Vector, or some data structure that another member of my team has created, I would have to go and create another comparator class for that.

The whole point here is to avoid writing a comparator or equals() method for every object. I just want to use reflection and go through every object's fields and compare them even if those fields are objects themselves. I am working with a rather large code base here, so writing a comparator class for each object type is pretty much out of the question.

Any advice would be greatly appreciated.

javidjam at 2007-6-29 18:45:35 > top of Java-index,Core,Core APIs...