Skip to content

Type Checking

Subtyping recap

Visit the official notes for a proper recap. A way to think of subtypes is with subsets. ColoredCircle <: Circle, and instances of ColoredCircles are a subset of all instances of Circles.

Here is a step-by-step guide on how to deal with type checking.

Summary of Steps

Type checking can be split into 2 main parts -- compile time and runtime check.

Compile Time

As the name suggests, we check whether this passes compilation or throws compilation error.

1
a = (C) b;
  1. Compiler find compile-time type of rhs b i.e. CTT(b)
  2. Check for possibility for run-time type of b to be subtype of C i.e. RTT(b) <: C
    • If impossible, exit with compilation error
  3. Find CTT of variable lhs a i.e. CTT(a)
  4. Check if C <: CTT(a)
    • If no, exit with compilation error
    • Else, add run-time check for RTT(b) <: C
  1. Find CTT(b)
  2. possible for RTT(b) <: C ? continue : compilation error
  3. Find CTT(a)
  4. Check if C <: CTT(a) ? runtime check : compilation error

Checks if typecast [(C) b] can potentially occur

  1. Compiler find compile-time type of b i.e. CTT(b)
  2. Check for possibility for run-time type of b to be subtype of C i.e. RTT(b) <: C
    • If impossible, exit with compilation error

Checks if assignment [a = expr] satisfies subtyping relationship

  1. Find CTT of variable a i.e. CTT(a)
  2. Check if C <: CTT(a)
    • If no, exit with compilation error
    • Else, add run-time check for RTT(b) <: C
  1. CTT(b) <: C
    • widening, always allowed
    • explicit type cast not needed
  2. C <: CTT(b)
    • narrowing, runtime check needed
    • If CTT(b) = B and RTT(b) = C (or subtype of C), allowed at runtime
    • If CTT(b) = B and RTT(b) = B (or other subtype of B that is not C), not allowed at runtime
    • Due to possibility, compiler adds code to check at runtime
  3. C is interface
    • Let CTT(b) = B, may have subclass B1 where B1 <: C (B1 implements C)
    • If RTT(b) = B1, allowed at runtime
  1. Let CTT(b) = B, B and C unrelated i.e. B </: C and C </: B
    • impossible for RTT(b) <: C (subclass of B extends B, cannot also extends C)
  2. C interface and B has final modifier
    • case 3 not possible, class cannot be inherited from

Runtime

Once the compile time check pass, compiler does a runtime check (added from step 4).

1
a = (C) b;
  1. Find runtime type of rhs i.e. RTT(b)
  2. Check if RTT(b) <: C

Step-by-step

Success

We will look at the following successful example first

Example 1
1
2
3
String str1 = "CS2030S";
Object obj = str1; // String <: Object
String str2 = (String) obj;
1
2
3
String str1 = "CS2030S";
Object obj = str1; // String <: Object
String str2 = (String) obj;
CTT(lhs) cast CTT(rhs)
Object
  1. Find compile-time type of obj i.e. CTT(obj)
1
2
3
String str1 = "CS2030S";
Object obj = str1; // String <: Object
String str2 = (String) obj;
CTT(lhs) cast CTT(rhs)
String Object
  1. Find compile-time type of obj i.e. CTT(obj)
  2. Check for possibility for run-time type of obj to be subtype of String i.e. RTT(obj) <: String
    • Case 2: String <: Object, continue
1
2
3
String str1 = "CS2030S";
Object obj = str1; // String <: Object
String str2 = (String) obj;
CTT(lhs) cast CTT(rhs)
String String Object
  1. Find compile-time type of obj i.e. CTT(obj)
  2. Check for possibility for run-time type of obj to be subtype of String i.e. RTT(obj) <: String
    • Case 2: String <: Object, continue
  3. Find CTT of str2 i.e. CTT(str2)
1
2
3
String str1 = "CS2030S";
Object obj = str1; // String <: Object
String str2 = (String) obj;
CTT(lhs) cast CTT(rhs)
String String Object
  1. Find compile-time type of obj i.e. CTT(obj)
  2. Check for possibility for run-time type of obj to be subtype of String i.e. RTT(obj) <: String
    • Case 2: String <: Object, continue
  3. Find CTT of str2 i.e. CTT(str2)
  4. Check if String <: str2
    • String <: String, compiles successfully. Runtime check for RTT(obj) <: String added.
1
2
3
String str1 = "CS2030S";
Object obj = str1; // String <: Object
String str2 = (String) obj;
cast RTT(rhs)
String String
  1. RTT(obj) is String
  2. Since String <: String, no error occurs

Interface

This example shows how it could work with interface

Example 2
1
2
3
4
5
6
7
8
interface Moveable{}
interface Runnable extends Moveable{}
class Animal implements Moveable{}
class Cheetah extends Animal implements Runnable{}

Cheetah c = new Cheetah();
Animal a = c; // Cheetah <: Animal
Moveable m = (Runnable) a;
1
2
3
4
5
6
7
8
interface Moveable{}
interface Runnable extends Moveable{}
class Animal implements Moveable{}
class Cheetah extends Animal implements Runnable{}

Cheetah c = new Cheetah();
Animal a = c; // Cheetah <: Animal
Moveable m = (Runnable) a;
CTT(lhs) cast CTT(rhs)
Animal
  1. Find compile-time type of a i.e. CTT(a)
1
2
3
4
5
6
7
8
interface Moveable{}
interface Runnable extends Moveable{}
class Animal implements Moveable{}
class Cheetah extends Animal implements Runnable{}

Cheetah c = new Cheetah();
Animal a = c; // Cheetah <: Animal
Moveable m = (Runnable) a;
CTT(lhs) cast CTT(rhs)
Runnable Animal
  1. Find compile-time type of a i.e. CTT(a)
  2. Check for possibility for run-time type of a to be subtype of Runnable i.e. RTT(a) <: Runnable
    • Case 3: Runnable is interface, no final modifier on Animal, continue
1
2
3
4
5
6
7
8
interface Moveable{}
interface Runnable extends Moveable{}
class Animal implements Moveable{}
class Cheetah extends Animal implements Runnable{}

Cheetah c = new Cheetah();
Animal a = c; // Cheetah <: Animal
Moveable m = (Runnable) a;
CTT(lhs) cast CTT(rhs)
Moveable Runnable Animal
  1. Find compile-time type of a i.e. CTT(a)
  2. Check for possibility for run-time type of a to be subtype of Runnable i.e. RTT(a) <: Runnable
    • Case 3: Runnable is interface, no final modifier on Animal, continue
  3. Find CTT of m i.e. CTT(m)
1
2
3
4
5
6
7
8
interface Moveable{}
interface Runnable extends Moveable{}
class Animal implements Moveable{}
class Cheetah extends Animal implements Runnable{}

Cheetah c = new Cheetah();
Animal a = c; // Cheetah <: Animal
Moveable m = (Runnable) a;
CTT(lhs) cast CTT(rhs)
Moveable Runnable Animal
  1. Find compile-time type of a i.e. CTT(a)
  2. Check for possibility for run-time type of a to be subtype of Runnable i.e. RTT(a) <: Runnable
    • Case 3: Runnable is interface, no final modifier on Animal, continue
  3. Find CTT of m i.e. CTT(m)
  4. Check if Runnable <: m
    • Runnable <: Moveable, compiles successfully. Runtime check for RTT(a) <: Runnable added.
1
2
3
4
5
6
7
8
interface Moveable{}
interface Runnable extends Moveable{}
class Animal implements Moveable{}
class Cheetah extends Animal implements Runnable{}

Cheetah c = new Cheetah();
Animal a = c; // Cheetah <: Animal
Moveable m = (Runnable) a;
cast RTT(rhs)
Runnable Cheetah
  1. RTT(a) is Cheetah
  2. Since Cheetah implements Runnable, Cheetah <: Runnable, no error occurs

Fail step 4

Here is an example where a compilation error occurs as it does not satisfy subtyping relationship

Example 3
1
2
3
ColoredCircle cc = new ColoredCircle();
Circle c1 = cc; // ColoredCircle <: Circle
Circle c2 = (Shape) c1;
1
2
3
ColoredCircle cc = new ColoredCircle();
Circle c1 = cc; // ColoredCircle <: Circle
Circle c2 = (Shape) c1;
CTT(lhs) cast CTT(rhs)
Circle
  1. Find compile-time type of c1 i.e. CTT(c1)
1
2
3
ColoredCircle cc = new ColoredCircle();
Circle c1 = cc; // ColoredCircle <: Circle
Circle c2 = (Shape) c1;
CTT(lhs) cast CTT(rhs)
Shape Circle
  1. Find compile-time type of c1 i.e. CTT(c1)
  2. Check for possibility for run-time type of c1 to be subtype of Shape i.e. RTT(c1) <: Shape
    • Case 1: Circle <: Shape, continue
1
2
3
ColoredCircle cc = new ColoredCircle();
Circle c1 = cc; // ColoredCircle <: Circle
Circle c2 = (Shape) c1;
CTT(lhs) cast CTT(rhs)
Shape Circle
  1. Find compile-time type of c1 i.e. CTT(c1)
  2. Check for possibility for run-time type of c1 to be subtype of Shape i.e. RTT(c1) <: Shape
    • Case 1: Circle <: Shape, continue
  3. Find CTT of c2 i.e. CTT(c2)
1
2
3
ColoredCircle cc = new ColoredCircle();
Circle c1 = cc; // ColoredCircle <: Circle
Circle c2 = (Shape) c1;
CTT(lhs) cast CTT(rhs)
Circle Shape Circle
  1. Find compile-time type of obj i.e. CTT(obj)
  2. Check for possibility for run-time type of obj to be subtype of String i.e. RTT(b) <: C
    • Case 1: Circle <: Shape, continue
  3. Find CTT of c2 i.e. CTT(c2)
  4. Check if Shape <: c2
    • Shape </: Circle, compilation error