CSE 331�Software Design & Implementation
Summer 2026
Section 6– Subtyping
Administrivia
Subtyping
Subtypes
Inheritance: Copy fields & methods from a superclass to a subclass
Java Subclassing: B extends A
Java Subtyping: B extends A or B implements A
class Pony {...}
class Unicorn extends Pony {...}
class RainbowDash extends Pony {...}
class Rarity extends Unicorn {...}
Pony
RainbowDash
Unicorn
Rarity
Behavioral Subtyping
Class B is a behavioral subtype of A if:
Crucially, Java cannot enforce behavioral subtyping!
Liskov Substitution Principle (LSP): When B is a behavioral subtype of A, one can use B wherever A is expected
Twilight is a subtype of Unicorn
void magic(Unicorn x) {...}
magic(new Rarity()) Safe!
magic(new Twilight ()) unsafe!
Task 1:�A Square and a Rectangle Walk Into a bar()...
Consider the following classes below to complete Task 4 on the worksheet. Justify your answers using the definition of behavioral subtyping.
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 ImmutableRectangle {
private int width;
private int height;
public int getWidth() { ... }
public int getHeight() { ... }
}
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 ImmutableSquare {
private int side;
public int getWidth() { ... }
public int getHeight() { ... }
}
Task 1:�A Square and a Rectangle Walk Into a bar()...
Comparison | Yes / No | Why? |
Is Square a behavioral subtype of Rectangle? | No | setWidth/setHeight have different specs |
Is Rectangle a behavioral subtype of ImmutableRectangle? | Yes | Has all required methods; extra methods are okay |
Is ImmutableSquare a behavioral subtype of ImmutableRectangle | Yes | Same observer methods and compatible specs |
Is ImmutableRectangle a behavioral subtype of Rectangle? | No | Missing setWidth and setHeight |
Equals and Hashcode
The equals method (review)
The specification for equals necessitates the following:
Several additional notions of equality:
The hashcode method (review)
The specification for hashcode necessitates the following:
x.hashCode() == y.hashCode()
Equal objects must have the same hash code:
Ideally a good hashCode method returns different values for unequal objects, but this is not required
Method Overriding
Overriding equals
public bool equals(Object o) {..}
Overriding
public bool equals(Object o) {..}
public bool equals(Object d) {..}
Overloading
public bool equals(Whale o) {..}
Task 2: Thing 1 and Thing 2
Consider the following class Thing, which, unfortunately, doesn't properly override equals, but overloads it instead.
class Thing {
private int contents;
public Thing(int value) {
this.contents = value;
}
public boolean equals(Thing other) {
return this.contents == other.contents;
}
}
Task 2: Thing 1 and Thing 2
Here is a program that uses class Thing. 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 the boxes to indicate your answers.
class Thing {
private int contents;
public Thing(int value) {
this.contents = value;
}
public boolean equals(Thing other) {
return this.contents == other.contents;
}
}
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));
}
Task 2: Thing 1 and Thing 2
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));
}
// Method called Output
// Object.equals true
// Thing.equals true
// Object.equals false
// Object.equals false
// Thing.equals true
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;
}
}
Task 3: Wrecked Angle
For each of the five possible hashCode functions for ColoredRectangle, 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. Circle the right answers.
(Hints: ^ is the exclusive-or arithmetic operation. Recall that if a.equals(b) is true, then a.hashCode() must equal b.hashCode())
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;
}
}
Task 3: Wrecked Angle
For each of the five possible hashCode functions for ColoredRectangle, 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. Circle the right answers.
(Hints: ^ is the exclusive-or arithmetic operation. Recall that if a.equals(b) is true, then a.hashCode() must equal b.hashCode())
public int hashCode() { return width; }
public int hashCode() { return color.hashCode(); }
public int hashCode() { return 42; }
public int hashCode() {
return width ^ height ^ color.hashCode();
}
public int hashCode() { return width ^ height; }
// Correct and adequate to good
// Not correct
// Correct but very poor
// Not correct
// Correct and adequate to good
End
Please turn in your half-sheets!