What is the relationship between open, abstract, and override?

← Override Mechanics · Ref: Q578

In EK9, three modifiers control method overriding: 'as open' makes a class extensible, 'as abstract' makes a class extensible with incomplete methods, and 'override' declares intent to replace.

CLOSED (DEFAULT)

Classes without 'as open' or 'as abstract' cannot be extended. Their methods cannot be overridden.

AS OPEN

Makes a class extensible. All public and protected methods become overridable in children:

  Widget as open
    draw() ...

Children can override draw() with 'override draw()'.

AS ABSTRACT

Makes a class extensible with some methods having no body:

  Shape as abstract
    area() as abstract
      <- rtn as Float?

Children MUST override abstract methods.

OVERRIDE CHAINS

A child that is 'as open' allows further overriding:

  Base as open -> Child extends Base as open -> GrandChild extends Child

Each level can override.

A child WITHOUT 'as open' stops the chain:

  Base as open -> FinalChild extends Base

FinalChild's methods cannot be overridden further.

See Q101 for closed by default. See Q102 for 'as open'. See Q103 for abstract classes. See Q570 for override basics. See Q575 for deep override chains.

Example

defines module qa.override.openabstract

  defines class

    //Abstract: must be extended, abstract methods must be overridden
    Shape as abstract
      area() as pure abstract
        <- rtn as Float?

      perimeter() as pure abstract
        <- rtn as Float?

      default operator ?

    //Open: can be extended further
    Rectangle extends Shape as open
      rectWidth <- 0.0
      rectHeight <- 0.0

      Rectangle()
        ->
          rectWidth as Float
          rectHeight as Float
        this.rectWidth: rectWidth
        this.rectHeight: rectHeight

      override area() as pure
        <- rtn as Float: rectWidth * rectHeight

      override perimeter() as pure
        <- rtn as Float: 2.0 * (rectWidth + rectHeight)

      rectWidth() as pure
        <- rtn as Float: rectWidth

      rectHeight() as pure
        <- rtn as Float: rectHeight

      default operator ?

    //Closed (default): cannot be extended further
    Square extends Rectangle
      Square()
        -> side as Float
        super(side, side)

      //Can override parent methods
      override area() as pure
        <- rtn as Float: rectWidth() * rectWidth()

      default operator ?

  defines program

    OpenAbstractOverrideDemo()
      stdout <- Stdout()

      square <- Square(5.0)
      stdout.println(`Square area: ${square.area()}`)
      stdout.println(`Square perimeter: ${square.perimeter()}`)

      //Polymorphic use
      shapes <- List() of Shape
      shapes += Rectangle(3.0, 4.0)
      shapes += Square(6.0)

      for shape in shapes
        stdout.println(`Area: ${shape.area()}, Perimeter: ${shape.perimeter()}`)

Common mistakes

E50060 — While extending Shape directly is not circular, Square is designed to reuse Rectangle. If Square tried to extend a type that creates a cycle, E50060 would fire. See ek9 -h E50060 for details.

Incorrect:

Square extends Shape

Correct:

Square extends Rectangle

E05120 — When overriding the abstract area method from Shape, the override keyword is mandatory. Without it, the compiler detects method shadowing. See ek9 -h E05120 for details.

Incorrect:

area() as pure

Correct:

override area() as pure

E05150 — The abstract area method in Shape is declared as pure, so every override must also be pure. Removing purity from the override violates the contract. See ek9 -h E05150 for details.

Incorrect:

override area()

Correct:

override area() as pure
Other ways to ask this
  • How do open and abstract interact with override?
  • When is a method overridable in EK9?
  • How do I design a class hierarchy for extension?

Coming from another language?

Java: classes open by default, final to close. Kotlin: classes and methods closed by default, open keyword on both. Swift: class and method level open/final control. EK9: class-level open/abstract, all public methods overridable in open classes.

Keywords: override, closed, chain, inherit, extensible, design, virtual, open, hierarchy, abstract