Why does EK9 reject circular type hierarchies?
← Classes and OOP · Ref: Q772
EK9 detects when types form a cycle in their inheritance chain — A extends B, B extends C, C extends A. This is logically impossible: you cannot inherit from something that inherits from you.
WHY CYCLES ARE IMPOSSIBLE
Inheritance means 'I am a specialisation of my parent'. If A extends B and B extends A, then A is a specialisation of B which is a specialisation of A — an infinite recursion with no base. Which constructor runs first? Which fields exist first? Undefined.
THIS EXAMPLE
The Vehicle, Car, ElectricCar hierarchy is a valid linear chain. Vehicle is the root, Car extends it, ElectricCar extends Car. No cycles.
COMMON CAUSES
1. Refactoring that accidentally swapped inheritance direction
2. Copy-paste errors when creating similar classes
3. Two classes that need each other's features (use composition instead)
HOW TO FIX
1. Draw the inheritance relationships to visualise the cycle
2. Identify the true root/base class
3. Break the cycle by removing one 'extends'
4. Consider composition over inheritance
See Q77 for inheritance. See Q85 for composition. See Q83 for closed types.
Example
defines module qa.classesandoop.circularhierarchy defines class Vehicle as open make <- String() Vehicle() -> m as String make :=: m describe() <- rtn as String: make default operator ? Car extends Vehicle as open doors <- Integer() Car() -> m as String d as Integer super(m) doors :=: d override describe() <- rtn as String: `${super.describe()} ${doors}-door` default operator ? ElectricCar extends Car as open range <- Integer() ElectricCar() -> m as String d as Integer r as Integer super(m, d) range :=: r override describe() <- rtn as String: `${super.describe()} EV range=${range}` default operator ? defines program ShowHierarchy() stdout <- Stdout() ev <- ElectricCar("Tesla", 4, 350) if ev? stdout.println(ev.describe())
Common mistakes
E05020 — Changing Car to extend ElectricCar creates a cycle: Car extends ElectricCar, ElectricCar extends Car. Draw the hierarchy and ensure it forms a tree with no loops. See ek9 -h E05020 for details.
Incorrect:
Car extends ElectricCar as open
Correct:
Car extends Vehicle as open
Other ways to ask this
- What triggers E05020 circular hierarchy detected?
- Why can't class A extend class B if B extends A?
- How do I fix a circular inheritance chain?
Coming from another language?
Java: detects circular inheritance at compile time with 'cyclic inheritance involving X'. Python: detects at runtime with TypeError during C3 linearization. Rust: no inheritance, so no circular hierarchies possible. Go: no inheritance, composition only. Kotlin: same as Java, compile-time detection. EK9: compile-time detection at TYPE_HIERARCHY_CHECKS phase.
Keywords: class, chain, circular, hierarchy, refactoring, E05020, composition, cycle, extends, inheritance