How do I define a generic class in EK9?

← Generics · Ref: Q194

EK9 uses natural-language syntax for generics with 'of type T'. Generic classes define type parameters that are filled in when the class is instantiated.

DEFINING A GENERIC CLASS

Use 'of type T' after the class name:

  Container of type T
    item as T?

T is the type parameter that gets replaced with a real type.

CONSTRUCTOR WITH TYPE PARAMETER

Constructors accept T-typed arguments:

  Container()
    -> val as T
    item :=? val

METHODS RETURNING T

Methods can return the type parameter:

  get()
    <- rtn as T: T()

INSTANTIATION WITH TYPE INFERENCE

The type parameter is inferred from the constructor argument:

  strContainer <- Container("hello")

Or explicitly specified:

  intContainer <- Container() of Integer

See Q58 for generic functions. See Q93 for class basics. See Q101 for closed by default. See Q195 for type constraints. See Q196 for multi-parameter generics. See Q197 for extending generics. See Q198 for built-in generics.

See Q642 for generic constructor inference. See Q653 for type independence.

Example

defines module qa.generics.classes

  defines class

    Container of type T
      item as T?

      Container() as pure
        item :=? T()

      Container() as pure
        -> initialItem as T
        item :=? initialItem

      get()
        <- rtn as T: T()
        rtn :=? T(item)

      default operator ?

  defines program

    GenericClassDemo()
      stdout <- Stdout()

      // === INSTANTIATION WITH TYPE INFERENCE ===

      strContainer <- Container("hello")
      stdout.println(`String container set: ${strContainer?}`)

      // === EXPLICIT TYPE SPECIFICATION ===

      intContainer <- Container(42)
      stdout.println(`Integer container set: ${intContainer?}`)

      // === DEFAULT CONSTRUCTION ===

      emptyContainer <- Container() of String
      stdout.println(`Empty container set: ${emptyContainer?}`)

      // === ACCESS VALUE ===

      retrieved <- strContainer.get()
      if retrieved?
        stdout.println(`Retrieved: ${retrieved}`)

Common mistakes

E06020 — Container has one type parameter T, so providing two type arguments is wrong. The number of type arguments must match the number of type parameters. See ek9 -h E06020 for details.

Incorrect:

emptyContainer <- Container() of (String, Integer)

Correct:

emptyContainer <- Container() of String
Other ways to ask this
  • How do generic types work in EK9?
  • How do I create a parameterized class in EK9?
  • What is the syntax for generics in EK9?

Coming from another language?

Java: class Container<T> with angle brackets, type erasure at runtime. C++: template<typename T> with full monomorphization. Rust: struct Container<T> with trait bounds. Go: type Container[T any] (Go 1.18+). Kotlin: class Container<T> similar to Java. EK9: 'Container of type T' reads as natural English, type inference from constructors.

Keywords: container, instantiate, parameter, class, define, inference, parameterized, type-parameter, generic, type