Why can't I extend a class in EK9?

← Classes and OOP · Ref: Q784

EK9 classes are CLOSED by default — they cannot be extended unless explicitly marked 'as open'. This is the opposite of Java (where classes are open by default) and the same as Kotlin.

WHY CLOSED BY DEFAULT

1. Extending a class creates tight coupling between parent and child
2. The parent must be designed for inheritance (fragile base class problem)
3. Composition is almost always better than inheritance
4. Built-in types (List, Dict, Optional) are deliberately closed

HOW TO MAKE EXTENSIBLE

  MyClass as open       // Now it can be extended
  MyClass as abstract   // Abstract classes are implicitly open

COMPOSITION ALTERNATIVE

Instead of extending a closed class, wrap it as a field:

  MyWrapper
    items as List of String
    add(item as String)
      items += item

THIS EXAMPLE

The Shape class is marked 'as open' so Circle can extend it. The Config class is closed — the program uses it directly.

See Q83 for why closed by default. See Q84 for the open modifier. See Q85 for composition.

Example

defines module qa.classesandoop.closedbydefault

  defines class

    Shape as open
      name <- String()

      Shape()
        -> n as String
        name :=: n

      describe()
        <- rtn as String: name

      default operator ?

    Circle extends Shape
      radius <- Float()

      Circle()
        ->
          n as String
          r as Float
        super(n)
        radius :=: r

      override describe()
        <- rtn as String: `${super.describe()} r=${radius}`

      default operator ?

    ItemHolder
      items <- List() of String

      addItem()
        -> item as String
        items += item

      count()
        <- rtn as Integer: length items

      default operator ?

  defines program

    ShowShapes()
      stdout <- Stdout()
      circle <- Circle("circle", 5.0)
      if circle?
        stdout.println(circle.describe())

      holder <- ItemHolder()
      holder.addItem("one")
      holder.addItem("two")
      stdout.println($holder.count())

Common mistakes

E05030 — Removing 'as open' makes Shape closed. Circle cannot extend a closed class. Either add 'as open' to Shape, or use composition instead of inheritance. See ek9 -h E05030 for details.

Incorrect:

    Shape
      name <- String()

Correct:

    Shape as open
      name <- String()

E05030 — List is a built-in closed type — it cannot be extended. Use composition: hold a List as a field instead of inheriting from it. See ek9 -h E05030 for details.

Incorrect:

    ItemList extends List of String

      addItem()

Correct:

      items <- List() of String

      addItem()
Other ways to ask this
  • What triggers E05030 not open to extension?
  • Why are EK9 classes closed by default?
  • How do I make a class extensible in EK9?

Coming from another language?

Java: classes open by default, use 'final' to close. Python: classes always open. Rust: no class inheritance. Kotlin: classes closed by default, use 'open' keyword (same as EK9). Go: no class inheritance, composition only. C#: classes open by default, use 'sealed' to close. EK9: closed by default like Kotlin, 'as open' to allow extension.

Keywords: final, sealed, extends, default, inheritance, open, composition, closed, E05030