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

Re: the benefits of immutability



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".)

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.

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