Skip to main content

CSE 331: Section 6 — Subtyping

Task 1 – A Square and a Rectangle Walk Into a bar()...

Consider the following classes.

public class Rectangle {
  private int width;
  private int height;

  public int getWidth() { ... }
  public int getHeight() { ... }

  // @effects this.width = w
  public void setWidth(int w) { ... }

  // @effects this.height = h
  public void setHeight(int h) { ... }
}
public class Square {
  private int side;

  public int getWidth() { ... }
  public int getHeight() { ... }

  // @effects this.side = w
  public void setWidth(int w) { ... }

  // @effects this.side = h
  public void setHeight(int h) { ... }
}
public class ImmutableRectangle {
  private int width;
  private int height;

  public int getWidth() { ... }
  public int getHeight() { ... }
}
public class ImmutableSquare {
  private int side;

  public int getWidth() { ... }
  public int getHeight() { ... }
}

For each comparison below, answer Yes or No and briefly justify your answer.

Comparison Yes / No
Is Square a behavioral subtype of Rectangle?
Why or why not?
Is Rectangle a behavioral subtype of ImmutableRectangle?
Why or why not?
Is ImmutableSquare a behavioral subtype of ImmutableRectangle?
Why or why not?
Is ImmutableRectangle a behavioral subtype of Rectangle?
Why or why not?

Task 2 - Thing 1 and Thing 2

Consider the following class Thing:

class Thing {
  private int contents;
  public Thing(int value) { this.contents = value; }
  public boolean equals(Thing other) { return this.contents == other.contents; }
}

Unfortunately Thing does not correctly override equals, but overloads it instead. As a result, there are two methods called equals, but with different signatures: equals(Object) and equals(Thing).

Here is a program that uses class Thing.

public static void main(String[] args) {
  Thing t = new Thing(17);
  Object o = t;
  Thing u = new Thing(17);

  System.out.println(t.equals(o));
  System.out.println(t.equals(u));
  System.out.println(o.equals(u));
  System.out.println(u.equals(o));
  System.out.println(u.equals((Thing) o));
}

For each System.out.println statement, indicate which equals method is called (equals(Object) or equals(Thing)), and whether the statement prints true or false. Check one box in each group.

Expression Which method is called? What is printed?
equals(Object) equals(Thing) true false
t.equals(o)
t.equals(u)
o.equals(u)
u.equals(o)
u.equals((Thing) o)

Task 3 - Wrecked Angle

Here is a small class with a correctly overridden equals method.

public class ColoredRectangle {
  private int width;  private int height;  private String color;

  /** equality for ColoredRectangles. Two ColoredRectangles
  *   are the same if they have the same width and height. */
  @Override
  public boolean equals(Object o) {
    if (! (o instanceof ColoredRectangle)) {
      return false;
    }
    ColoredRectangle cr = (ColoredRectangle) o;
    return this.width == cr.width && this.height == cr.height;
  }
}

Below are five possible hashCode functions for ColoredRectangle. For each one indicate if it is incorrect (does not satisfy the specification for hashCode), or if it is correct but very poor, or correct and adequate-to-good. Check one box for each. (Hints: ^ is the exclusive-or arithmetic operation. Recall that if a.equals(b) is true, then a.hashCode() must equal b.hashCode().)

  1. public int hashCode() { return width; }
    
    Not correct Correct but very poor Correct and adequate-to-good
  2. public int hashCode() { return color.hashCode(); }
    
    Not correct Correct but very poor Correct and adequate-to-good
  3. public int hashCode() { return 42; }
    
    Not correct Correct but very poor Correct and adequate-to-good
  4. public int hashCode() { return width ^ height ^ color.hashCode(); }
    
    Not correct Correct but very poor Correct and adequate-to-good
  5. public int hashCode() { return width ^ height; }
    
    Not correct Correct but very poor Correct and adequate-to-good