找出导致equals()返回false的原因
-
10-07-2019 - |
题
如何找出导致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()
中实际访问的字段的简单问题