views:

748

answers:

10

How can I find out what caused equals() to return false?

I'm not asking about a sure-way, always right approach, but of something to aid in the development process. Currently I have to step into the equals() calls (usually a tree of them) until one of them is false, then step into it, ad nauseam.

I thought about using the object graph, outputting it to xml and comparing the two objects. However, XMLEncoder requires default constructors, jibx requires pre-compilation, x-stream and simple api are not used in my project. I don't mind copying a single class, or even a package, into my test area and using it there, but importing a whole jar for this just isn't going to happen.

I also thought about building an object graph traverser myself, and I might still do it, but I'd hate to start dealing with special cases (ordered collections, non-ordered collections, maps...)

Any idea how to go about it?

Edit: I know adding jars is the normal way of doing things. I know jars are reusable units. However, the bureaucracy needed (at my project) for this doesn't justify the results - I'd keep on debugging and stepping into.

+2  A: 

It sounds like you want java-diff, or something like it.

MarkusQ
+5  A: 

It's presumably not a full graph comparison... unless your equals include every property in each class ... (you could try == :))

Try hamcrest matchers - you can compose each matcher in an "all of" matcher:

Matcher<MyClass> matcher = CoreMatchers.allOf(
  HasPropertyWithValue.hasProperty("myField1", getMyField1()),
  HasPropertyWithValue.hasProperty("myField2", getMyField2()));
if (!matcher.matches(obj)){
  System.out.println(matcher.describeFailure(obj));
  return false;
}
return true;

It will say things like: 'expected myField1 to have a value of "value" but was "a different value"'

Of course you can inline the static factories. This is a bit heavier than using apache-commons EqualsBuilder, but it does give you an accurate description of exactly what failed.

You can create your own specialised matcher for quickly creating these expressions. It would be wise to copy apache-commons EqualsBuilder here.

BTW, the hamcrest basic jar is 32K (including source!) giving you the option of reviewing the code and saying to your bosses "I'll stand by this as my own code" (which I presume is your import problem).

Stephen
+1  A: 

I don't mind copying a single class, or even a package, into my test area and using it there, but importing a whole jar for this just isn't going to happen.

Um... what? Adding a jar to your classpath is, if anything, easier and less disturbing to the project than copying classes, or entire packages as source code.

As for your specific problem, do you have a lot of different classes that use many different properties to determine equality, or do you just a have deeply nested object graph of essentially the same classes? In the latter case, it would be very easy to just strucutre the equals() methods so that you can put breakpoints on the "return false" statements. In the former case, this might be too much work, I suppose. But then, an XML-based comparison may not work either, since it will show differences between semantically equal objects (e.g. Sets and Maps).

Michael Borgwardt
A: 

I know adding jars is the normal way of doing things. I know jars are reusable units. However, the bureaucracy needed (at my project) for this doesn't justify the results - I'd keep on debugging and stepping into.

One way around this is to include a library like Spring (which pulls in loads of other jar) I have seen Spring project which didn't actually use any Spring jar just so they could use any jar which is bundled with it.

Peter Lawrey
+3  A: 

You could use aspects to patch "equal" on the classes in your object graph and make them log the object state to a file when they return false. To log the object state you could use something like beanutils to inspect the object and dump it. This is one jar-based solution which can be easily used in your workspace

If the hierarchy of the objects stored in your tree is simple enough, you could place conditional breakpoints on your classes "equal" implementation which only triggers when "equal" returns false which would limit the number of times you have to step into ... you can use this wherever you have debugger access. eclipse handles this just fine.

Jean
For example, AspectJ: http://www.eclipse.org/aspectj/
jdigital
A: 

commons-jxpath might be useful to quickly examine the object tree. I don't fully understand the issue of including jars, but you can just use it in your own project in whatever IDE you use that presumably lets you use expressions during debugging.

ykaganovich
A: 

Maybe this article about method tracing will help you.

How it Works

A custom class loader reads the class file and instruments each method with tracing code. The class loader also adds a static field to each class. This field has two states, 'on' and 'off'. The tracing code checks this field prior to printing. The command line options access and modify this static field to control tracing output.

The sample output they show looks promising for your problem, as it shows the return value of some methods (like isApplet):

isApplet=false

It should be easy for you to spot the exact class that started to return false in equals. Here's the complete sample output from the page:

% java -jar /home/mike/java/trace.jar -classpath "/home/mike/jdk1.3/demo/jfc/SwingSet2/SwingSet2.jar" -exclude CodeViewer SwingSet2
|SwingSet2.()
|SwingSet2.
|SwingSet2.main([Ljava.lang.String;@1dd95c)
||isApplet(SwingSet2@3d12a6)
||isApplet=false
||SwingSet2.createFrame(apple.awt.CGraphicsConfig@93537d)
||SwingSet2.createFrame=javax.swing.JFrame@cb01e3
||createSplashScreen(SwingSet2@3d12a6)
|||createImageIcon(SwingSet2@3d12a6, "Splash.jpg", "Splash.accessible_description")
|||createImageIcon=javax.swing.ImageIcon@393e97
|||isApplet(SwingSet2@3d12a6)
|||isApplet=false
|||getFrame(SwingSet2@3d12a6)
|||getFrame=javax.swing.JFrame@cb01e3
|||getFrame(SwingSet2@3d12a6)
|||getFrame=javax.swing.JFrame@cb01e3
||createSplashScreen
.run(SwingSet2$1@fba2af)
..showSplashScreen(SwingSet2@3d12a6)
...isApplet(SwingSet2@3d12a6)
...isApplet=false
..showSplashScreen
.run
||initializeDemo(SwingSet2@3d12a6)
|||createMenus(SwingSet2@3d12a6)
||||getString(SwingSet2@3d12a6, "MenuBar.accessible_description")
|||||getResourceBundle(SwingSet2@3d12a6)
|||||getResourceBundle=java.util.PropertyResourceBundle@6989e
||||getString="Swing demo menu bar"

lothar
+1  A: 

Given that your project can't add a jar, it seems quite beyond a SO answer to give a whole implementation of a solution that other projects take a substantial amount of code to accomplish (and nicely include in a Jar for you).

How about a non-code solution - conditional breakpoints in the debugger? You could add breakpoints that trip only if the method returns false, and put them on all the relevant classes. No stepping.

Yishai
A: 

XMLEncoder only encodes bean properties, whereas equals can, obviously, work on non-beans and and any internal fields.

Part of the problem is that you don't know what equals is actually looking at. An object could have many diffrent fields and still claim it was equal to some other object, it may even be a diffrent type. (for example, a custom URL class might return true for a string that equals it's external form).

So I don't think it's possible without bytecode instrumentation where you can actually modify the classes equals() function to see what fields it accesses. Even then, it would still be extreemly difficult to 'truly' know why a function returned false. But hopefully it would be a simple issue of comparing the fields that are actually accessed in equals()

Chad Okere
+2  A: 

Okay, this is a completely bizarre way of looking at it, but how about introducing a new static method:

public static boolean breakableEquals(Object o1, Object o2)
{
    if (o1 == o2)
    {
        return true;
    }
    if (o1 == null || o2 == null)
    {
        return false;
    }
    // Don't condense this code!
    if (o1.equals(o2))
    {
        return true;
    }
    else
    {
        return false;
    }
}

I know, the last bit looks mad... but the difference is that you can put a breakpoint on the "return false". If you then use breakableEquals in all your deep equality comparisons then you can break as soon as you hit the first "return false".

This doesn't help much if you're comparing lots of primitive values, admittedly... but it might be helpful. I can't say I've ever actually used this, but I can't see why it wouldn't work. It will be a bit less efficient, of course - so if you're dealing with high-performance code you may want to change it afterwards.

Another option would be to use something like:

boolean result = // comparison;
return result;

Assuming your IDE supports them, you can then put a conditional breakpoint on the return statement, and set the condition to be "!result".

Yet another option:

public static boolean noOp(boolean result)
{
    return result;
}

You can then use this within comparisons:

return Helpers.noOp(x.id == y.id) &&
       Helpers.noOp(x.age == y.age);

I'd hope that when you're not debugging, this would be optimised away by the JIT - but again, you can use a conditional breakpoint in noOp. It makes the code uglier, unfortunately.

In short: no particularly appealing solutions here, but just some ideas which might help in certain situations.

Jon Skeet