What are the return type and parameter type rules for method overrides?

← Override Mechanics · Ref: Q699

EK9 requires that override methods match the parent method signature exactly. Return types and parameter types must be identical.

RETURN TYPE MATCHING (E05180)

The return type of an override must match the parent exactly:

  Parent: process() <- rtn as String
  Child:  override process() <- rtn as String    // Correct
  Child:  override process() <- rtn as Integer   // E05180

PARAMETER TYPE MATCHING (E05170)

Parameter types must also match exactly:

  Parent: handle(input as String)
  Child:  override handle(input as String)    // Correct
  Child:  override handle(input as Integer)   // E05170

PURITY CANNOT BE ADDED (E05160)

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 EXACT MATCHING

Exact matching ensures Liskov Substitution. Any code using the parent type will work correctly with any descendant.

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.
      Return types and parameter types set here.
    -?>
    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}`

      default operator ?

    <?-
      Correct override: all types match exactly.
      Return types, parameter types, and purity all preserved.
    -?>
    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}`

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

Common mistakes

E05150 — If the parent method is pure, the override must remain pure. Removing purity from an override is not allowed. Conversely, adding purity to an override of a non-pure method also triggers E05150. 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 match the parent exactly. EK9 does not support covariant return types, so changing String to Integer 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 E05170 parameter type mismatch in override?
  • What is E05180 return type mismatch 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: all types must match exactly (no covariance).

Keywords: virtual, type, override, E05180, E05170, E05160, return, covariance, signature, open, parameter, function, abstract, inherit