What is method shadowing and how does EK9 handle it?

← Type Hierarchy Constraints · Ref: Q606

'Shadowing' usually means a child class declaring a method that silently replaces or hides a parent method. EK9 splits this into three cases and decides each one by the SIGNATURE (name plus parameter types):

1. SAME NAME, SAME SIGNATURE, NO 'override' - E05120
The child method would replace the parent's, but without saying so. EK9 rejects it with E05120: an accidental override is not allowed. Add 'override' if replacement is what you mean, or rename the method if it is not.

2. SAME NAME, SAME SIGNATURE, WITH 'override' - a proper override
Calls through a parent-typed reference reach the child's version: the object decides which method runs.

3. SAME NAME, DIFFERENT SIGNATURE - an overload, which is legal
A child may add format(text, prefix) beside the inherited format(text). Both are available on the child; a call picks the one whose parameters match. It is NOT an override, so marking it 'override' is E05110 (it does not override anything).

WHY THIS MATTERS

In Java, forgetting @Override means a typo in a parameter type silently creates a new method, and a matching signature without the annotation silently overrides. EK9 makes the intent explicit: an override must say 'override' (E05120 otherwise), and 'override' must really match a parent signature (E05110 otherwise).

WHAT TO DO

If replacing parent behaviour: use 'override' with the EXACT same parameter types. If adding behaviour: an overload with different parameters is fine, or use a distinct, descriptive name when the meaning differs.

See Q570 for override basics. See Q571 for missing override keyword. See Q572 for false override claims.

Example

#!ek9
defines module qa.typehierarchy.methodshadow

  defines class

    Formatter as open
      format() as pure
        -> text as String
        <- rtn as String: text

      describe() as pure
        <- rtn as String: "base formatter"

      default operator ?

    UpperFormatter extends Formatter
      //CORRECT: override with exact same signature
      override format() as pure
        -> text as String
        <- rtn as String: text.upperCase()

      override describe() as pure
        <- rtn as String: "upper formatter"

      //CORRECT: an overload - same name, different parameters, no 'override'
      format() as pure
        ->
          text as String
          prefix as String
        <- rtn as String: prefix + text.upperCase()

      //CORRECT: new method with a different name (no collision)
      formatWithPrefix() as pure
        -> text as String
        <- rtn as String: ">> " + text.upperCase()

      default operator ?

  defines function

    testProperOverride()
      base <- Formatter()
      upper <- UpperFormatter()

      require base.format("hello") == "hello"
      require upper.format("hello") == "HELLO"
      require upper.formatWithPrefix("hello") == ">> HELLO"
      require upper.format("hello", "# ") == "# HELLO"

      //Polymorphic: calling through parent type still dispatches correctly
      formatters <- List() of Formatter
      formatters += Formatter()
      formatters += UpperFormatter()

      for formatter in formatters
        result <- formatter.format("test")
        require result?

Common mistakes

E05120 — UpperFormatter.format has the same signature as Formatter.format, so it replaces it and must say so with 'override'. Without it, the compiler reports E05120. See ek9 -h E05120 for details.

Incorrect:

format() as pure

Correct:

override format() as pure

E05110 — Adding override to formatWithPrefix would be a false claim since Formatter has no method named formatWithPrefix. The compiler rejects override when no parent method matches. See ek9 -h E05110 for details.

Incorrect:

override formatWithPrefix() as pure

Correct:

formatWithPrefix() as pure
Other ways to ask this
  • What is E05120 in EK9?
  • How does EK9 detect method shadowing?
  • Can a child class have a method with the same name but a different signature in EK9?

Coming from another language?

Java: a matching signature overrides with or without @Override; a different signature is an overload. C++: hiding is allowed by default, 'override' keyword optional. C#: 'new' keyword can explicitly shadow (EK9 has no such keyword). Python: no override concept, last definition wins. Kotlin: 'override' required, similar to EK9. Swift: 'override' required, similar to EK9. EK9: a same-signature method without 'override' is E05120; 'override' on a different signature is E05110; a different signature without 'override' is a legal overload.

Keywords: E05120, abstract, child, circular, collision, function, hierarchy, inherit, name, open, overload, override, parent, shadow, shadowing, signature, substitution, type, virtual