[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: the benefits of immutability




On Mittwoch, August 20, 2003, at 03:46  Uhr, Vadim Nasardinov wrote:

> Pascal Costanza wrote:
> > public boolean equals(Object that) {
> >    if ((this.class != that.class) &&
> >        this.class.isAssignableFrom(that.class))
> >      return that.equals(this);
> >
> >    if (!(that instanceof Subclass)) return false;
> >
> >    return super.equals(that) && (this.state == 
> ((Subclass)that).state);
> > }
>
> Clever.  I can't find any holes in this, as far as the equivalence
> relationship is concerned.  (One minor nitpick is that you have to
> test for nullity, before you can dereference "that".)

Right.

> If this approach is used for implementing internally facing
> package-scoped classes that are not part of the exported API, then
> everything is fine.  If classes like this *are* part of the exported
> API, then users of such a class may be in for a surprise.  Let's
> ground this in slightly more concrete terms.  Suppose the following
> classes follow the above approach and are part of the API that you
> ship as part of your product.
>
>  public class Circle {
>      private int radius;
>
>      /**
>       *@pre radius > 0
>       **/
>      public Circle(int radius) {
>          if ( radius <=0 ) { throw new IllegalArgumentException(); }
>          this.radius = radius;
>      }
>
>      public int getRadius() {
>          return radius;
>      }
>      // the rest of the methods omitted for brevity
>  }
>
> Suppose Barbara is a user of your product who writes code against the
> Circle API.  In her world, any circle with a radius of 3 is equal to
> any other circle with a radius of 3.  When Jeannette is hired she
> extends Circle as follows.
>
>  public class ColoredCircle extends Circle {
>      private Color color;
>
>      public ColoredCircle(int radius, Color color) {
>          super(radius);
>          this.color = color;
>      }
>
>      public Color getColor() {
>          return color;
>      }
>  }
>
> Since Jeannette reads ll1-discuss on a regular basis, she dutifully
> follows the method you suggested.  Barbara's code no longer works,
> because color-blind circles are never equal to colored circles of the
> same radius.  A circle with a radius of 3 is not the same thing as,
> say, a circle with a radius of 3.

Of course, I expect Jeanette to think about what she does and write the 
following code:

public boolean equals(Object that) {
   if (that == null) return false;

   if ((this.class != that.class) &&
        this.class.isAssignableFrom(that.class))
     return that.equals(this);

   return super.equals(that) &&
              (that instance of ColoredCircle ?
                 this.color == ((ColoredCircle)that).color : true);
}


> Which brings us back to the the original point: writing externally
> facing classes that allow subclassing is hard.

I think the really issue here is that Java doesn't have multi-methods. 
Both tricks I have introduced in this example are hand-coded 
simulations of multiple dispatch.


Pascal