What are abstract classes and methods in EK9?

← Classes and OOP · Ref: Q103

Abstract classes define incomplete types that must be extended with concrete implementations. They can have both implemented and abstract methods, and they can hold state (properties).

ABSTRACT CLASSES

Mark a class as abstract with 'as abstract':

  Formatter as abstract
    prefix <- String()
    format() as abstract
      -> text as String
      <- rtn as String?

Abstract classes cannot be instantiated directly (E50080) — they are incomplete types. 'as abstract' implies 'as open' — you never need both.

WHICH CONSTRUCTS SUPPORT AS ABSTRACT?

Five constructs support 'as abstract': functions, records, classes, components, and traits. Abstract functions are unique to EK9 among mainstream languages. A method without a body must be declared 'as abstract' (E07110).

ABSTRACT METHODS

Abstract methods have no body. Subclasses must provide implementations using the mandatory 'override' keyword:

  override format()
    -> text as String
    <- rtn as String: prefix + data

Omitting 'override' triggers E05120. The compiler ensures all abstract methods are implemented — omitting any triggers E07140.

ABSTRACT FUNCTIONS

EK9 also supports abstract functions:

  defines function
    Parser as abstract
      -> input as String
      <- result as String?

Concrete functions extend abstract ones with 'is' or 'extends'.

WHEN TO USE ABSTRACT VS TRAIT

Use abstract class when: you need shared state (properties), constructors, and partial implementation with a single inheritance chain.
Use trait when: you need pure behaviour contracts that multiple unrelated classes can implement. EK9 supports multiple traits but single class inheritance.

See Q102 for 'as open'. See Q101 for closed by default. See Q51 for abstract functions. See Q106 for traits. See Q110 for trait vs abstract comparison. See Q111 for components. See Q263 for factory pattern returning abstract class implementations. See Q560 for purity contracts on abstract methods. See Q570 for override mechanics. See Q580 for constructor delegation in class hierarchies.

Example

defines module qa.oop.abstractclasses

  defines function

    Parser as pure abstract
      -> input as String
      <- result as String?

    JsonParser is Parser as pure
      -> input as String
      <- result as String: "parsed:" + input

  defines class

    Formatter as abstract
      prefix <- String()

      Formatter()
        -> prefix as String
        this.prefix: prefix

      format() as abstract
        -> text as String
        <- rtn as String?

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

      default operator ?

    BracketFormatter extends Formatter
      BracketFormatter()
        super("[")

      override format()
        -> text as String
        <- rtn as String: `${prefix()}${text}]`

      default operator ?

    HtmlFormatter extends Formatter
      tag <- "p"

      HtmlFormatter()
        -> tag as String
        super(`<${tag}>`)
        this.tag: tag

      override format()
        -> text as String
        <- rtn as String: `${prefix()}${text}</${tag}>`

      default operator ?

  defines program

    AbstractDemo()
      stdout <- Stdout()

      // === ABSTRACT CLASS with concrete subclass ===

      bracket <- BracketFormatter()
      stdout.println(bracket.format("hello"))

      html <- HtmlFormatter("div")
      stdout.println(html.format("content"))

      // === ABSTRACT FUNCTION ===

      result <- JsonParser("data")
      stdout.println(result)

      // === Polymorphism ===

      formatters <- List() of Formatter
      formatters += BracketFormatter()
      formatters += HtmlFormatter("span")

      for formatter in formatters
        stdout.println(formatter.format("test"))

Common mistakes

E05120 — When implementing an abstract method from a parent class, the 'override' keyword is mandatory. Omitting it on BracketFormatter's format() triggers E05120. See ek9 -h E05120 for details.

Incorrect:

format()

Correct:

override format()

E50010 — Only abstract or open classes can be extended. Abstract classes like Formatter are automatically open. Extending a closed class triggers E50010 — not open to be extended. See ek9 -h E50010 for details.

Incorrect:

BracketFormatter extends SomeClosedClass

Correct:

BracketFormatter extends Formatter

E07110 — A method without a body must be declared 'as abstract'. Omitting the modifier triggers E07110 — implementation not provided so must be declared as abstract. See ek9 -h E07110 for details.

Incorrect:

format()
        -> text as String
        <- rtn as String?

Correct:

format() as abstract
        -> text as String
        <- rtn as String?

E50020 — A class cannot extend a function because they are different construct types (genus). Classes extend classes and functions extend functions. See ek9 -h E50020 for details.

Incorrect:

BracketFormatter extends JsonParser

Correct:

BracketFormatter extends Formatter

E07130 — BracketFormatter extends abstract Formatter but must implement all abstract methods. Removing the override of format() leaves the abstract method unimplemented, triggering E07130 — all abstract methods must be implemented in concrete subclasses. See ek9 -h E07130 for details.

Incorrect:

      default operator ?

Correct:

      override format()
        -> text as String
        <- rtn as String: `${prefix()}${text}]`

      default operator ?

E50080 — Abstract classes cannot be instantiated directly — they are incomplete types with unimplemented methods. Attempting to call Formatter() triggers E50080 — cannot make a call on an abstract function/type directly. Create a concrete subclass instead. See ek9 -h E50080 for details.

Incorrect:

fmt <- Formatter()

Correct:

bracket <- BracketFormatter()

E50040 — An abstract method can only exist inside an abstract class. If the class is not marked 'as abstract' but contains an abstract method, E50040 is triggered — cannot be abstract in a non-abstract construct. Either mark the class 'as abstract' or provide a method body. See ek9 -h E50040 for details.

Incorrect:

Formatter as open
      prefix <- String()

Correct:

Formatter as abstract
      prefix <- String()

E50080 — Abstract classes cannot be instantiated directly — they are incomplete types with unimplemented methods. Attempting to call Formatter() triggers E50080 — cannot make a call on an abstract function/type directly. Create a concrete subclass instead. See ek9 -h E50080 for details.

Incorrect:

html <- Formatter("div")

Correct:

html <- HtmlFormatter("div")
Other ways to ask this
  • How do I create an abstract class in EK9?
  • How do abstract methods work in EK9?
  • When should I use abstract vs trait in EK9?

Coming from another language?

Java: abstract class with abstract methods, can have state and constructors, single inheritance. Python: ABC with @abstractmethod decorator, no enforcement at parse time. Rust: no abstract classes, use traits with default implementations. Go: no abstract types, interfaces are implicitly implemented. Kotlin: abstract class with abstract members, can have state, single inheritance. EK9: 'as abstract' modifier, abstract methods with no body, abstract functions (unique), automatically open for extension, subclasses use 'override'.

Keywords: open, component, method, concrete, class, abstract, virtual, trait, implement, incomplete, override, state, record, hierarchy, function, is, extends, object-oriented