How do I make a class extensible with 'as open'?

← Classes and OOP · Ref: Q102

In EK9, use 'as open' to allow a class to be extended. Without it, classes are closed by default (E05030). Abstract classes are automatically open since they must be extended to be useful.

THE AS OPEN MODIFIER

Mark a class as open to allow subclasses:

  Shape as open
    name <- String()

Now other classes can extend Shape. Without 'as open', attempting to extend Shape triggers E05030.

WHICH CONSTRUCTS SUPPORT AS OPEN?

Five constructs support 'as open': functions, records, classes, components, and traits. Traits are inherently open (designed to be implemented) so 'as open' on a trait is accepted for syntax consistency but has no additional effect. Services and text blocks cannot be extended.

ABSTRACT CLASSES ARE AUTOMATICALLY OPEN

Abstract classes are inherently open because they require concrete subclasses:

  Vehicle as abstract
    speed() as abstract
      <- rtn as Float?

'as abstract' implies 'as open' — you never need both.

INHERITANCE SYNTAX

Use 'extends' or 'is' to inherit:

  Circle extends Shape
  Circle is Shape

Both are equivalent. Use 'override' for overridden methods — it is mandatory in EK9, not optional like in Java.

OVERRIDE IS MANDATORY

Unlike Java where @Override is an annotation you can forget, EK9 requires the 'override' keyword on any method that overrides a parent method. Omitting it triggers E05120. This prevents accidental shadowing.

SEALED ALTERNATIVE: ALLOW ONLY

If you want limited extensibility rather than fully open, use 'allow only' to restrict which classes can extend:

  Shape allow only Circle, Square, Triangle as open

This is EK9's equivalent of Java 17's sealed classes. See Q298-Q301 for details.

WHEN TO USE AS OPEN

Use 'as open' when you deliberately design a type for extension. Keep open types small in number — most types should remain closed. Prefer composition (Q109) and traits (Q106) over inheritance where possible.

See Q101 for why types are closed by default. See Q103 for abstract classes. See Q93 for class basics. See Q106 for traits. See Q570 for override mechanics in open class hierarchies. See Q298 for sealed traits with 'allow only'. See Q301 for sealed classes.

Example

defines module qa.oop.asopen

  defines class

    Shape as open
      name <- "shape"

      Shape()
        -> name as String
        this.name: name

      name() as pure
        <- rtn as String: name

      describe()
        <- rtn as String: "I am a " + name

      default operator

    Circle extends Shape
      radius <- 0.0

      Circle()
        -> radius as Float
        super("circle")
        this.radius: radius

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

      override describe()
        <- rtn as String: `I am a circle with radius ${radius}`

      default operator ?

    Vehicle as abstract
      speed() as abstract
        <- rtn as Float?

      describe()
        <- rtn as String: `Vehicle at ${speed()} mph`

      default operator ?

    Car is Vehicle
      topSpeed <- 120.0

      Car()
        -> topSpeed as Float
        this.topSpeed: topSpeed

      override speed()
        <- rtn as Float: topSpeed

      default operator ?

  defines record

    Coordinate
      xPos as Float: 0.0
      yPos as Float: 0.0

      default operator ?

  defines program

    AsOpenDemo()
      stdout <- Stdout()

      // === AS OPEN: explicit extension ===

      shape <- Shape("triangle")
      stdout.println(shape.describe())

      circle <- Circle(5.0)
      stdout.println(circle.describe())

      // === ABSTRACT: automatically open ===

      car <- Car(150.0)
      stdout.println(car.describe())

      // === OVERRIDE ===

      stdout.println(`Circle name: ${circle.name()}`)
      stdout.println(`Car speed: ${car.speed()}`)

Common mistakes

E05120 — When overriding a method from a parent class, the 'override' keyword is mandatory. Omitting it on Circle's describe() triggers E05120 because the method exists in Shape. See ek9 -h E05120 for details.

Incorrect:

describe()

Correct:

override describe()

E05030 — Without 'as open', Shape would be closed by default and Circle could not extend it. Attempting to extend a closed class triggers E05030 — not open to be extended. EK9 types are closed by default to prevent the fragile base class problem. See ek9 -h E05030 for details.

Incorrect:

Shape

Correct:

Shape as open

E05120 — Car must use 'override' when implementing the abstract method speed() from Vehicle. Omitting 'override' triggers E05120. See ek9 -h E05120 for details.

Incorrect:

speed()

Correct:

override speed()

E50020 — A class defined in 'defines class' cannot extend a record from 'defines record' because they are different construct types (genus). Classes extend classes, records extend records. See ek9 -h E50020 for details.

Incorrect:

Circle extends Coordinate

Correct:

Circle extends Shape

E50060 — Circle's constructor calls super("circle") which expects Shape's single-String constructor. Vehicle has a different constructor signature (make, year). Extending Vehicle with Shape's super call triggers E50060 — constructor not resolved. See ek9 -h E50060 for details.

Incorrect:

Circle extends Vehicle

Correct:

Circle extends Shape
Other ways to ask this
  • How do I allow a class to be extended in EK9?
  • What does 'as open' mean on a class?
  • How does inheritance work in EK9?

Coming from another language?

Java: all classes are open by default, use 'final' to close; @Override annotation is optional (easily forgotten); sealed classes added in Java 17 with 'permits'. Python: all classes open, no way to seal. Rust: no inheritance at all, traits only. Go: no inheritance, embedding only. Kotlin: classes final by default, 'open' keyword to allow extension (same approach as EK9), 'override' keyword mandatory (same as EK9). Swift: classes not final by default but 'final' keyword available. EK9: closed by default, 'as open' to allow extension, 'as abstract' is automatically open, 'extends' or 'is' for inheritance, 'override' mandatory not optional, 'allow only' for sealed types.

Keywords: function, modifier, child, object-oriented, abstract, record, extensible, only, extends, component, sealed, is, parent, allow, override, inherit, super, base, open, class, define, virtual, inheritance, subclass