Lecture: 14 Date: Feb 11, 2008 Announcements: Review session after class in lab. Equality -------- Every class in Java automatically extends the Object class, which has a method called equals. Unfortunately it doesn't quite do what you might want it to: CartPt p1 = new CartPt(0,0); CartPt p2 = new CartPt(0,0); p1.equals(p2) ==> false Why? equals returns true when this object and the given object are the literally the same object, not when they are identical objects. Think of identical twins. equals says is this object the same person as that object? twins might be indistinguishable, but they're not the same person. So equality is difficult. Also, it's not always clear what "identical" means. If we represent sets with lists, then set {1,2} is identical to the set {2,1}, but if just compare lists element by element, we get the wrong answer. In each case we have to consider what it means to be "the same". Let's write our own sameCartPt method for determining if two Cartesian points are the same, ie. they have the same x and y value. In CartPt --------- // is this Cartesian point the same as the given one? boolean sameCartPt(CartPt that) { return this.x == that.x && this.y == that.y; } What about Circles and Rectangles? We'll say a rectangle is the same as another one if both have the same location, height, and width. Two circles are the same if the have the same center point and radius. In Rectangle ------------ // is this rectangle the same as the given rectangle? // same location, same size. boolean sameRect(Rectangle that) { return this.loc.sameCartPt(that.loc) && this.height == that.height && this.width == that.width; } In Circle --------- // is this circle the same as the given circle? boolean sameCirc(Circle that) { return this.center.sameCartPt(that.center) && this.radius == that.radius; } But now, what if want a general sameShape method that works on the union of Rectangles and Circles? // Represents a shape. interface IShape { // is this shape the same as the given shape? boolean sameShape(IShape that); } We say two shapes are the same if they are both the same rectangle, or both the same circle. But how can we decide if "that" (the given shape) is a rectangle or a circle? And even if we could tell, how can we call a Rectangle (or Circle) specific method? There are (at least) two solutions. We look at one today. Let's abstract: abstract class AShape implements IShape { abstract boolean sameShape(IShape that); } Now, I want to add sameCirc and sameRect to the abstract class. In the default case, I want these methods to produce false: abstract class AShape implements IShape { boolean sameRect(Rectangle that) { return false; } boolean sameCirc(Circle that) { return false; } abstract boolean sameShape(IShape that); } Now we know that given an AShape, we can call sameRect and sameCirc, moreover, Circle overrides sameCirc and Rectangle overrides sameRect. We still have to implement sameShape in Rectangle and Circle: In Rectangle ------------ // is this rectangle the same as the given shape? boolean sameShape(IShape that) { ... } What do we know here? this is a Rectangle. that may be a Rectangle OR it may be a Circle -- but we know it has a sameRect method. Suppose we call that.sameRect -- which method definition is invoked? - if that is a Rectangle, then the overriding definition in the Rectangle class is invoked, ie. the one that compares location, height width. - that that is a Circle, then the definition from the abstract method is invoked since Circle didn't override sameRect. And the abstract method always returns false, but since this is a Rectangle and that is a Circle, we've done the right thing. So the method looks like this: In Rectangle ------------ // is this rectangle the same as the given shape? boolean sameShape(IShape that) { return that.sameRect(this); } Notice that sameRect expects a Rectangle, but this IS a Rectangle, so we're OK. sameShape in Circle is symmetric to this case in Rectangle. We'll see another approach next time. Here's the complete program we've developed: // Represent a cartesian point. class CartPt { int x; int y; CartPt(int x, int y) { this.x = x; this.y = y; } // is this point the same as that one? boolean sameCartPt(CartPt that) { return this.x == that.x && this.y == that.y; } } // Represents a shape. interface IShape { // is this shape the same as the given shape? boolean sameShape(IShape that); boolean sameCirc(Circle that); boolean sameRect(Rectangle that); } abstract class AShape implements IShape { boolean sameRect(Rectangle that) { return false; } boolean sameCirc(Circle that) { return false; } abstract boolean sameShape(IShape that); } // Represent a rectangle. class Rectangle extends AShape { CartPt loc; int height; int width; Rectangle(CartPt loc, int height, int width) { this.loc = loc; this.height = height; this.width = width; } // is this rectangle the same as the given shape? boolean sameShape(IShape that) { return that.sameRect(this); } // is this the same rectangle as that? boolean sameRect(Rectangle that) { return this.loc.sameCartPt(that.loc) && this.height == that.height && this.width == that.width; } } // Represents a circle. class Circle extends AShape { CartPt center; int radius; Circle(CartPt center, int radius) { this.center = center; this.radius = radius; } // is this circle the same as the given shape? boolean sameShape(IShape that) { return that.sameCirc(this); } // is this the same circle as that? boolean sameCirc(Circle that) { return this.center.sameCartPt(that.center) && this.radius == that.radius; } } class Examples { Examples() {} CartPt p1 = new CartPt(0,0); CartPt p2 = new CartPt(0,0); CartPt p3 = new CartPt(0,1); boolean sameCartPt = (check this.p1.sameCartPt(this.p2) expect true) && (check this.p1.sameCartPt(this.p3) expect false); Rectangle r1 = new Rectangle(p1, 10, 10); Rectangle r2 = new Rectangle(p2, 10, 10); Rectangle r3 = new Rectangle(p1, 9, 10); boolean sameRect = (check this.r1.sameRect(this.r2) expect true) && (check this.r1.sameRect(this.r3) expect false); Circle c1 = new Circle(p1, 5); Circle c2 = new Circle(p2, 5); Circle c3 = new Circle(p3, 5); boolean sameCirc = (check this.c1.sameCirc(this.c2) expect true) && (check this.c1.sameCirc(this.c3) expect false); boolean sameShape = (check this.c1.sameShape(this.c2) expect true) && (check this.c1.sameShape(this.c3) expect false) && (check this.c1.sameShape(this.r1) expect false) && (check this.r1.sameShape(this.c1) expect false); }