What are the return type and parameter type rules for method overrides?
← Override Mechanics · Ref: Q699
An override keeps the parent's PARAMETER types exactly, keeps its purity, and may NARROW its return type to a subtype (a covariant return).
RETURN TYPE: SAME OR NARROWER (E07440)
The return type of an override must be the parent's type or a subtype of it:
Parent: duplicate() <- rtn as Formatter Child: override duplicate() <- rtn as Formatter // Correct Child: override duplicate() <- rtn as DetailFormatter // Correct: covariant Child: override duplicate() <- rtn as Integer // E07440
An unrelated return type is E07440 ('return types are incompatible (covariance required)'). Code holding a Formatter reference still gets a Formatter back, so the narrowed override is safe - and it is the override that runs.
PARAMETER TYPES: EXACTLY THE SAME (E05110)
Parameter types must match exactly:
Parent: handle(input as String) Child: override handle(input as String) // Correct Child: override handle(input as Integer) // E05110
Different parameter types make a new overload, not an override, so 'override' on it is E05110.
PURITY IS KEPT (E05150, E05160)
A pure parent method requires a pure override (E05150). If the parent method is NOT pure, the override cannot add purity:
Parent: compute() // Not pure Child: override compute() as pure // E05160
Purity must be designed in at the base level.
WHY
Any code using the parent type works correctly with any descendant: it passes the same argument types, and it gets back something that IS the type it expects.
See Q570 for override basics. See Q573 for access modifier rules. See Q567 for purity chain inheritance.
Example
defines module qa.override.returntypecovariance defines class <?- Base class defining the method contract. Parameter types and purity set here; return types may be narrowed. -?> Formatter as open //Method with String return and String parameter formatItem() -> itemText as String <- formatted as String: `[${itemText}]` //Pure method: descendants must keep purity summarize() as pure <- rtn as String: "base summary" //Method with multiple parameters combine() -> firstPart as String secondPart as String <- combined as String: `${firstPart} ${secondPart}` //Method returning the base type duplicate() <- rtn as Formatter: Formatter() default operator ? <?- Correct overrides: parameter types and purity preserved exactly. A return type may be narrowed to a subtype (covariant return). -?> DetailFormatter extends Formatter //Correct: String -> String, same types override formatItem() -> itemText as String <- formatted as String: `<< ${itemText} >>` //Correct: pure override of pure method override summarize() as pure <- rtn as String: "detail summary" //Correct: (String, String) -> String, same types override combine() -> firstPart as String secondPart as String <- combined as String: `${firstPart} | ${secondPart}` //Correct: covariant return - DetailFormatter is a Formatter override duplicate() <- rtn as DetailFormatter: DetailFormatter() default operator ? defines program ReturnTypeCovarianceDemo() stdout <- Stdout() base <- Formatter() stdout.println(base.formatItem("item")) stdout.println(base.summarize()) detail <- DetailFormatter() stdout.println(detail.formatItem("item")) stdout.println(detail.summarize()) stdout.println(detail.combine(firstPart: "left", secondPart: "right")) //Through a Formatter reference, the covariant override still runs asBase as Formatter: DetailFormatter() stdout.println(`Duplicate: ${asBase.duplicate().summarize()}`)
Common mistakes
E05150 — If the parent method is pure, the override must remain pure; removing purity from an override triggers E05150. (The reverse - adding purity to an override of a non-pure method - is E05160.) See ek9 -h E05150 for details.
Incorrect:
override summarize()
Correct:
override summarize() as pure
E05110 — Parameter types in an override must match the parent exactly. Changing String to Integer means the method no longer overrides the parent and triggers E05110. See ek9 -h E05110 for details.
Incorrect:
override formatItem() -> itemText as Integer
Correct:
override formatItem() -> itemText as String
E07440 — The return type of an override must be the parent's type or a subtype of it (covariant). Integer is unrelated to String, so the override triggers E07440. See ek9 -h E07440 for details.
Incorrect:
override formatItem() -> itemText as String <- formatted as Integer: 42
Correct:
override formatItem() -> itemText as String <- formatted as String: `<< ${itemText} >>`
Other ways to ask this
- What is E05160 cannot add purity in override?
- What is E05110 parameter type mismatch in override?
- What is E07440 incompatible return type in override?
- Must override methods have identical signatures?
Coming from another language?
Java: covariant return types allowed, parameter types must match. Kotlin: covariant returns, exact params. C++: covariant returns. Rust: no inheritance, trait impls must match exactly. EK9: like Java - exact parameter types (E05110), covariant (narrowed) return types allowed, an unrelated return type is E07440.
Keywords: return, E05160, override, type, virtual, E05110, inherit, abstract, function, parameter, E07440, open, signature, covariance