How do override and access modifiers interact in a three-level class hierarchy?
← Override Mechanics · Ref: Q676
When overriding in multi-level class hierarchies, three rules must hold at every level: the override keyword must be present, the access modifier must be the same or wider, and the signature must match exactly. These rules apply equally to METHODS and OPERATORS.
OVERRIDE ON METHODS AND OPERATORS
The override keyword works identically on both:
override describe() as pure method override override operator ? as pure operator override override operator $ as pure operator override
If the parent defines it, the child must say override.
OVERRIDE KEYWORD REQUIRED (E05120)
Any method or operator that replaces a parent version MUST use 'override'. Without it, the compiler reports E05120. This applies to operator ? (inherited from base type), operator $ (if parent defines it), and all other operators.
ACCESS MODIFIER MATCHING (E05130)
An override can maintain or WIDEN access. Narrowing access (public to private) violates Liskov and triggers E05130.
SIGNATURE MATCHING (E05140)
Parameter types and return types must match.
THREE-LEVEL CHAIN
Grandparent declares the contract. Parent overrides it. Grandchild overrides it again. Each level must independently satisfy override, access, and signature rules — for both methods and operators.
See Q570 for override basics. See Q575 for deep override chains. See Q579 for trait override chains.
Example
defines module qa.override.accesshierarchy defines class <?- Level 1: Grandparent declares protected methods. These form the contract for all descendants. -?> Grandparent as abstract protected describe() <- rtn as String: "grandparent" protected classify() as pure abstract <- rtn as String? default operator ? <?- Level 2: Parent overrides with matching access. Protected stays protected, override keyword present. -?> Parent extends Grandparent as open override protected describe() <- rtn as String: "parent" override protected classify() as pure <- rtn as String: "parent-class" default operator ? <?- Level 3: Child overrides again, maintaining contract. Each level independently satisfies all three rules. -?> Child extends Parent override protected describe() <- rtn as String: "child" override protected classify() as pure <- rtn as String: "child-class" default operator ? defines program OverrideAccessHierarchyDemo() stdout <- Stdout() child <- Child() stdout.println(`Override chain verified: ${child?}`)
Common mistakes
E07260 — Without the 'override' keyword, the compiler treats 'protected describe()' as a new method declaration that shadows the parent method, triggering E07260. See ek9 -h E07260 for details.
Incorrect:
protected describe()
Correct:
override protected describe()
E07010 — An override must maintain or widen the access level. Narrowing from protected to private violates Liskov Substitution and triggers E07010. See ek9 -h E07010 for details.
Incorrect:
override private classify() as pure
Correct:
override protected classify() as pure
E05110 — The override signature must match the parent exactly. Adding, removing, or changing parameter types means the method does not actually override the parent and triggers E05110. See ek9 -h E05110 for details.
Incorrect:
override protected describe() -> extra as Integer <- rtn as String: "child"
Correct:
override protected describe() <- rtn as String: "child"
Other ways to ask this
- What is E05120 method signature mismatch in override?
- What is E05130 access modifier mismatch in override?
- What is E05140 covariant return type required in override?
- How does override protected work in deep class hierarchies?
Coming from another language?
Java: @Override annotation, access can widen (private to protected), signatures must match. Kotlin: 'override' keyword mandatory, access widening allowed. Rust: no inheritance, trait impl must match. Python: no override keyword, duck typing. EK9: 'override' keyword mandatory, access must match or widen, signature must match exactly.
Keywords: access, virtual, override, protected, class, hierarchy, chain, signature, E05140, open, E05130, abstract, E05120, inherit