What is method shadowing and how does EK9 prevent it?

← Type Hierarchy Constraints · Ref: Q606

Method shadowing occurs when a child class declares a method with the same name as a parent method but a different signature (different parameter types or count). This creates confusing behavior where calling through a parent reference invokes the parent method, but calling through a child reference invokes the child method.

WHY SHADOWING IS DANGEROUS

Shadowing violates the substitution principle. If child.process() does something completely different from parent.process(), code expecting parent behavior through a parent-typed reference will behave differently from code using a child-typed reference. This is a major source of bugs.

E05120: SHADOWING DETECTION

EK9 detects shadowing and raises E05120. If a child method has the same name as a parent method but a different signature, it's not a valid override (signatures don't match) and it's not a new method (name collision). The compiler rejects it.

WHAT TO DO INSTEAD

If replacing parent behavior: use 'override' with the EXACT same signature. If creating a new method: rename it to avoid collision with the parent method name. If you need both behaviors: keep both methods but give them different, descriptive names.

OVERRIDE VS SHADOW

A proper override uses the 'override' keyword with an identical signature. A shadow accidentally matches the name without matching the signature. EK9 requires explicit intent via 'override', preventing accidental shadowing.

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

Example

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

      //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 — When the child UpperFormatter has a method matching parent Formatter.format, the override keyword is required. Without it, the compiler detects method shadowing. 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?
  • Why can't I have a method with the same name but different signature in a child class?

Coming from another language?

Java: shadowing is allowed (with @Override catching some cases, but not all). C++: hiding is allowed by default, 'override' keyword optional. C#: 'new' keyword can explicitly shadow (EK9 bans this entirely). Python: no override concept, last definition wins. Kotlin: 'override' required, similar to EK9. Swift: 'override' required, similar to EK9. EK9: shadowing is a compile error (E05120), override keyword required for intentional replacement.

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