如何找出导致equals()返回false的原因?

我不是在问一个确定的方法,总是正确的方法,而是一些有助于开发过程的东西。目前我必须进入equals()调用(通常是它们的树),直到其中一个为假,然后进入它,令人作呕。

我考虑过使用对象图,将其输出到xml并比较两个对象。但是,XMLEncoder需要默认构造函数,jibx需要预编译,x-stream和简单的api不在我的项目中使用。我不介意将一个类,甚至一个包复制到我的测试区域并在那里使用它,但是导入整个jar只是不会发生。

我还想过自己构建一个对象图遍历器,我可能仍然会这样做,但我讨厌开始处理特殊情况(有序集合,非有序集合,映射......)

知道如何去做吗?

编辑:我知道添加罐子是正常的做事方式。我知道罐子是可重复使用的单位。然而,(在我的项目中)所需的官僚机构并不能证明结果是合理的 - 我会继续调试并踩到。

有帮助吗?

解决方案

这可能不是一个完整的图表比较...除非你的等于包括每个类中的每个属性......(你可以尝试== :))

尝试 hamcrest匹配器 - 您可以在<!>中编写每个匹配器。全部<!>匹配器:

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;

它会说:'预期myField1的值为<!>“;值<!>”;但<!>是一个不同的值<!>“;”

当然,您可以内联静态工厂。这比使用 apache-commons要重一点EqualsBuilder ,但确实可以准确描述失败的原因。

您可以创建自己的专用匹配器,以快速创建这些表达式。复制 apache-commons EqualsBuilder是明智之举这里。

BTW,hamcrest基本jar是32K(包括来源!),让你可以选择查看代码并对你的老板说“<!>”;我会支持这个作为我自己的代码<!> (我认为这是你的进口问题)。

其他提示

您可以使用方面来修补<!>“;等于<!>”;对象图中的类,并在返回false时将对象状态记录到文件中。要记录对象状态,可以使用beanutils之类的东西来检查对象并将其转储。这是一个基于jar的解决方案,可以在您的工作区中轻松使用

如果树中存储的对象的层次结构足够简单,则可以在类<!>“等于<!>”上放置条件断点。只有在<!>“;等于<!>”时才触发的实现返回false会限制您必须进入的次数...您可以在调试器访问的任何地方使用它。 eclipse处理这个很好。

听起来你想要 java-diff ,或类似的东西它

好的,这是一种看待它的完全奇怪的方式,但是如何引入一种新的静态方法:

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;
    }
}

我知道,最后一点看起来很疯狂......但区别在于你可以在<!>上加一个断点;返回false <!>“;如果您在所有深度相等比较中使用breakableEquals,那么只要您点击第一个<!>“return false <!>”,就可以中断。

如果您正在比较许多原始值,这无济于事,不可否认......但它可能会有所帮助。我不能说我曾经实际使用过这个,但我不明白为什么它不起作用。当然,它的效率会低一些 - 所以如果你正在处理高性能代码,你可能希望之后改变它。

另一种选择是使用类似的东西:

boolean result = // comparison;
return result;

假设您的IDE支持它们,您可以在return语句上放置一个条件断点,并将条件设置为<!> quot; !result <!> quot;。

又一个选择:

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

然后你可以在比较中使用它:

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

我希望当你没有调试时,这会被JIT优化掉 - 但同样,你可以在noOp中使用条件断点。不幸的是,它使代码变得更加丑陋。

简而言之:这里没有特别吸引人的解决方案,只是某些可能在某些情况下提供帮助的想法。

  

我不介意复制一个班级,   甚至包裹,进入我的测试区域   并在那里使用它,但导入一个   对于这个整个罐子是不会的   发生。

嗯......什么?将jar添加到类路径(如果有的话)比复制类或整个包作为源代码更容易,更不干扰项目。

至于你的具体问题,你是否有很多不同的类使用许多不同的属性来确定相等性,或者你只是有一个基本上相同类的深度嵌套的对象图?在后一种情况下,只需构造equals()方法就可以很容易,这样就可以在<!>上放置断点了;返回false <!> quot;声明。在前一种情况下,我认为这可能是太多的工作。但是,基于XML的比较可能也不起作用,因为它将显示语义上相等的对象(例如集合和地图)之间的差异。

鉴于你的项目不能添加一个jar,看起来完全没有SO答案给出一个解决方案的整个实现,其他项目需要大量的代码才能完成(并且很好地包含在Jar中)

非代码解决方案 - 调试器中的条件断点怎么样?您可以添加仅在方法返回false时跳转的断点,并将它们放在所有相关类上。没有踩踏。

  

我知道添加罐子是正常的做事方式。我知道罐子是可重复使用的单位。然而,(在我的项目中)所需的官僚机构并不能证明结果是合理的 - 我会继续调试并踩到。

这方面的一个方法就是包含一个像Spring这样的库(其中包含其他jar的内容)我看过Spring项目实际上并没有使用任何Spring jar,所以他们可以使用任何捆绑它的jar。

commons-jxpath 可能对快速检查对象树很有用。我不完全理解包含jar的问题,但你可以在你自己的项目中使用它,无论你使用什么IDE,大概都允许你在调试时使用表达式。

也许关于方法跟踪的文章可能会对您有所帮助。

  

工作原理

     

自定义类加载器读取类文件并使用跟踪代码检测每个方法。类加载器还为每个类添加一个静态字段。该字段有两个状态,“开”和“关”。跟踪代码在打印之前检查此字段。命令行选项访问并修改此静态字段以控制跟踪输出。

它们显示的示例输出看起来很有希望解决您的问题,因为它显示了某些方法的返回值(如isApplet):

  

= isApplet假

你应该很容易发现在equals中开始返回false的确切类。 这是页面的完整示例输出:

  

%java -jar /home/mike/java/trace.jar -classpath <!> quot; /home/mike/jdk1.3/demo/jfc/SwingSet2/SwingSet2.jar <!> quot; -exclude CodeViewer SwingSet2
  | SwingSet2()结果。   |。SwingSet2结果   | SwingSet2.main([Ljava.lang.String; @ 1dd95c)点击   || isApplet(SwingSet2 @ 3d12a6)点击   || isApplet =虚假点击   || SwingSet2.createFrame(apple.awt.CGraphicsConfig@93537d)点击   ||SwingSet2.createFrame=javax.swing.JFrame@cb01e3结果   || createSplashScreen(SwingSet2 @ 3d12a6)点击   ||| createImageIcon(SwingSet2 @ 3d12a6,<!> quot; Splash.jpg <!> quot;,<!> quot; Splash.accessible_description <!> quot;)
  |||createImageIcon=javax.swing.ImageIcon@393e97结果   ||| isApplet(SwingSet2 @ 3d12a6)点击   ||| isApplet =虚假点击   |||的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 =虚假点击   ..showSplashScreen结果   .RUN结果   || initializeDemo(SwingSet2 @ 3d12a6)点击   ||| createMenus(SwingSet2 @ 3d12a6)点击   |||| getString(SwingSet2 @ 3d12a6,<!> quot; MenuBar.accessible_description <!> quot;)
  ||||| getResourceBundle(SwingSet2 @ 3d12a6)点击   |||||getResourceBundle=java.util.PropertyResourceBundle@6989e结果   |||| getString = <!>“Swing演示菜单栏<!>”;

XMLEncoder只编码bean属性,而equals显然可以在非bean和任何内部字段上工作。

问题的一部分是你不知道实际上看到的是什么。一个对象可能有许多不同的字段,仍然声称它等于某个其他对象,它甚至可能是一个不同的类型。 (例如,对于与其外部表单相等的字符串,自定义URL类可能返回true。)

所以如果没有字节码检测,您可以在其中实际修改类equals()函数以查看它访问的字段。即便如此,“真正”知道函数返回false的原因仍然非常困难。但希望这是一个比较equals()

中实际访问的字段的简单问题
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top