{"id":88,"date":"2012-03-06T16:40:02","date_gmt":"2012-03-06T05:40:02","guid":{"rendered":"http:\/\/www.lazydungeon.com\/blog\/?p=88"},"modified":"2012-03-06T16:40:41","modified_gmt":"2012-03-06T05:40:41","slug":"subtle-equals-bug-in-java","status":"publish","type":"post","link":"https:\/\/www.lazydungeon.com\/blog\/subtle-equals-bug-in-java\/","title":{"rendered":"Subtle equals bug in Java"},"content":{"rendered":"<p>The <a href=\"http:\/\/findbugs.sourceforge.net\/\" target=\"_blank\">findbugs\u00a0<\/a>fairy discovered a little issue in my projects.<\/p>\n<blockquote><p><strong>Bug:<\/strong> com.bfm.appl.blimp.marketdata.FXPricingSecurity overrides equals in PricingSecurity and may not be symmetric<br \/>\n<strong>Pattern<\/strong><strong>id:<\/strong> EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC, <strong>type:<\/strong> Eq, <strong>category:<\/strong> CORRECTNESS<\/p>\n<p>This class defines an equals method that overrides an equals method in a superclass. Both equals methods methods use <code>instanceof<\/code> in the determination of whether two objects are equal. This is fraught with peril, since it is important that the equals method is symmetrical (in other words, <code>a.equals(b) == b.equals(a)<\/code>). If B is a subtype of A, and A&#8217;s equals method checks that the argument is an instanceof A, and B&#8217;s equals method checks that the argument is an instanceof B, it is quite likely that the equivalence relation defined by these methods is not symmetric.<\/p><\/blockquote>\n<p>Clear as mud? Let&#8217;s have a look at an example:<\/p>\n<p><!--more--><\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nclass StarFighter {\r\n    string name;\r\n    \/\/bunch of other star fighter related stuff here\r\n    ...\r\n    @Override\r\n    public boolean equals(Object obj) {\r\n        if (obj == null) return false;\r\n        if (!(obj instanceof StarFighter)) return false;\r\n\r\n        return this.name.equals(((StarFighter)obj).name);\r\n    }\r\n}\r\n<\/pre>\n<p>So far so good, we have our beautiful StarFighter class ready to roll, and the equals method works, more or less.<\/p>\n<p>However what if we what to specialise it a bit, say get our plan for a X-wing StarFighter going.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nclass XWing extends StarFighter {\r\n    \/\/bunch of cool XWing stuff\r\n    ...\r\n    @Override\r\n    public boolean equals(Object obj) {\r\n        if (obj == null) return false;\r\n        if (!(obj instanceof XWing)) return false;\r\n\r\n        return this.name.equals(((XWing)obj).name);\r\n    }\r\n}\r\n<\/pre>\n<p>Oh no! Here&#8217;s where we violate the symmetrical property of equals function.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n\r\nStarFighter s1 = new StarFighter();\r\nStarFighter x1 = new XWing();\r\ns1.setName(&quot;Steve&quot;);\r\nx1.setName(&quot;Steve&quot;);\r\nassertFalse(x1.equals(s1)); \/\/ yep, x1 is non-equivalent to a star fighter.\r\nassertFalse(s1.equals(x1)); \/\/ fail... \r\n\r\n<\/pre>\n<p>This is pretty unpredictable behaviours right here. Depends on which StarFighter you use as the base for compare, you get complete opposite results. <\/p>\n<p>How did I fixed this? In our case it made sense to make StarFighter into an abstract class and removed the equals method, and let the YWing class to implement it&#8217;s own equals. Which actually made more sense, so as it turns out, the bug was an indication of poor architecture.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The findbugs\u00a0fairy discovered a little issue in my projects. Bug: com.bfm.appl.blimp.marketdata.FXPricingSecurity overrides equals in PricingSecurity and may not be symmetric Patternid: EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC, type: Eq, category: CORRECTNESS This class defines an equals method that overrides an equals method in a superclass. Both equals methods methods use instanceof in the determination of whether two objects are equal. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[26,21],"class_list":["post-88","post","type-post","status-publish","format-standard","hentry","category-debug","tag-debug","tag-java-2"],"_links":{"self":[{"href":"https:\/\/www.lazydungeon.com\/blog\/wp-json\/wp\/v2\/posts\/88","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lazydungeon.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.lazydungeon.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.lazydungeon.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lazydungeon.com\/blog\/wp-json\/wp\/v2\/comments?post=88"}],"version-history":[{"count":6,"href":"https:\/\/www.lazydungeon.com\/blog\/wp-json\/wp\/v2\/posts\/88\/revisions"}],"predecessor-version":[{"id":116,"href":"https:\/\/www.lazydungeon.com\/blog\/wp-json\/wp\/v2\/posts\/88\/revisions\/116"}],"wp:attachment":[{"href":"https:\/\/www.lazydungeon.com\/blog\/wp-json\/wp\/v2\/media?parent=88"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.lazydungeon.com\/blog\/wp-json\/wp\/v2\/categories?post=88"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lazydungeon.com\/blog\/wp-json\/wp\/v2\/tags?post=88"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}