1 of 19

CSE 331�Software Design & Implementation

Summer 2026

Section 6– Subtyping

2 of 19

Administrivia

  • HW5 will release TONIGHT and will be due next week!

3 of 19

Subtyping

4 of 19

Subtypes

Inheritance: Copy fields & methods from a superclass to a subclass

Java Subclassing: B extends A

  • Reusing or extending implementations

Java Subtyping: B extends A or B implements A

  • Type compatibility and allowed substitution

  • Ex: Every Unicorn is a Pony

class Pony {...}

class Unicorn extends Pony {...}

class RainbowDash extends Pony {...}

class Rarity extends Unicorn {...}

Pony

RainbowDash

Unicorn

Rarity

5 of 19

Behavioral Subtyping

Class B is a behavioral subtype of A if:

  • B has all the methods of A
  • Each method of B has a stronger spec than A’s corresponding methods

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

  • Ex: Suppose Rarity is a behavioral subtype of Unicorn, but

Twilight is a subtype of Unicorn

void magic(Unicorn x) {...}

magic(new Rarity()) Safe!

magic(new Twilight ()) unsafe!

6 of 19

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() { ... }

}

7 of 19

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

8 of 19

Equals and Hashcode

9 of 19

The equals method (review)

The specification for equals necessitates the following:

  • Reflexive: x.equals(x) = true
  • Symmetric: x.equals(y) = y.equals(x)
  • Transitive: x.equals(y) and y.equals(z) implies x.equals(z)
  • Consistent: x.equals(y) (never changes)
  • Null uniqueness: x.equals(null) = false

Several additional notions of equality:

  • Referential: Two objects are equal if they exact same in memory
  • Behavioral: Two objects are equal if there is no sequence of operations that distinguish them
  • Observational: Two objects are equal if there is no sequence of observer operations that distinguish them

10 of 19

The hashcode method (review)

The specification for hashcode necessitates the following:

  • Self-consistent: x.hashCode() shouldn’t change
  • Equality-consistent: If x.equals(y), then

x.hashCode() == y.hashCode()

Equal objects must have the same hash code:

  • Implementations of equals and hashCode work together
  • If you override equals, you must also override hashCode

Ideally a good hashCode method returns different values for unequal objects, but this is not required

11 of 19

Method Overriding

  • A subclass method overrides a superclass method when:
    • They have the exact same method name
    • They have the exact same argument types
      • Argument names do not matter

  • An overriding method should satisfy the overridden spec’s method

  • Always uses @override tag when overriding a method

  • Note that method overloading is not the same as overriding
    • Overloading: Same method name, but different argument types

12 of 19

Overriding equals

  • To override, you must have the same name and argument types

  • For equals, you must have one argument with type Object

  • A different argument type results in method overloading!

public bool equals(Object o) {..}

Overriding

public bool equals(Object o) {..}

public bool equals(Object d) {..}

Overloading

public bool equals(Whale o) {..}

13 of 19

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;

}

}

14 of 19

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));

}

15 of 19

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

16 of 19

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;

}

}

17 of 19

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;

}

}

18 of 19

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

19 of 19

End

Please turn in your half-sheets!