dev@glassfish.java.net

Re: non-symmetric equals method

From: Kedar Mhaswade <kedar.java.net_at_gmail.com>
Date: Thu, 22 Apr 2010 08:12:07 -0700

Josh says in his book (Page 38, second edition of Effective Java) that there
is no way to extend an instantiable class and add a value component while
preserving equals method, unless you are willing to forgo the benefits of
object-oriented abstraction. (I believe he says the same thing in the first
edition as well).

I think a closer analysis of Gregor's implementation is needed, but I think
in certain cases, it may not work. For example, a HashSet example that Josh
has demonstrated in his book may say that a ColoredPoint (1,0, Color.RED)
may not lie on a UnitCircle (a set of Points that lie on the classic unit
circle). (Refer page 39).

Josh further proposes in his book that there is a work-around that exists
and that is to favor composition over inheritance (e.g. Let ColorPoint
contain a Point and not extend it). But I don't think this is applicable if
you have already committed to inheritance.

Stephen -- I am not so sure of the infinite recursion. For two independent
subclasses of Point (ScentedPoint and ColorPoint), the equals contract
should hold with correctly coded equals methods.

I have attached my experimental classes.

On Wed, Apr 21, 2010 at 10:02 AM, Bill Shannon <bill.shannon_at_oracle.com>wrote:

> I'm looking into bugs found by the latest version of FindBugs.
> An error that has occurred in a few places is a non-symmetric equals
> method. FindBugs describes the error like this:
>
> 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.
> This is fraught with peril, since it is important that the equals
> method is symmetrical (in other words, a.equals(b) ==
> b.equals(a)). If B is a subtype of A, and A's equals method
> checks that the argument is an instanceof A, and B'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.
>
>
> There's quite a few descriptions of the problem on the web, along with some
> proposed solutions:
>
> http://www.drdobbs.com/java/184405053
> http://tal.forum2.org/equals
> http://www.artima.com/lejava/articles/equality.html
>
> http://stackoverflow.com/questions/27581/overriding-equals-and-hashcode-in-java
>
>
> Have others run into this same problem? How have you solved it?
>
>
> I hear that the second edition of Josh's Effective Java book has a
> solution. I don't have a copy, can someone who does look this up
> for me?
>
>
> The approach that looks the best to me so far is this described by
> Gregor Zeitlinger in
> http://www.artima.com/forums/flat.jsp?forum=226&thread=259279
>
>
> There's a version that doesn't require canEqual and is even more flexible:
>
> Point.equals(Object o) {
> if (o instanceof Point) {
> if (!o.getClass.isAssignableFrom(getClass())) {
> //let the subclass decide if we are equal
> return o.equals(this);
> }
> Point p = (Point)o;
> return x == p.x && y == p.y;
> }
> return false;
> }
>
>
> now ColoredPoint could be defined in either way:
>
> ColoredPoint.equals(Object o) {
> if (o instanceof ColoredPoint) {
> if (!o.getClass.isAssignableFrom(getClass())) {
> //let the subclass decide if we are equal
> return o.equals(this);
> }
> ColoredPoint p = (ColoredPoint)o;
> return color == p.color && super.equals(o);
> }
> return false;
> }
>
> or
>
> ColoredPoint.equals(Object o) {
> if (o instanceof ColoredPoint) {
> if (!o.getClass.isAssignableFrom(getClass())) {
> //let the subclass decide if we are equal
> return o.equals(this);
> }
> ColoredPoint p = (ColoredPoint)o;
> return color == p.color && super.equals(o);
> } else if (o instanceof Point) {
> return color == Color.RED && super.equals(o);
> }
> return false;
> }
>
>
>
> What do others think?
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe_at_glassfish.dev.java.net
> For additional commands, e-mail: dev-help_at_glassfish.dev.java.net
>
>