How does EK9 ensure class fields are properly initialized?

← Data Flow Safety · Ref: Q692

EK9 requires all class fields to be initialized either inline at declaration or through constructors. Uninitialized fields trigger E08180.

INLINE INITIALIZATION

The simplest approach initializes fields at declaration:

  name <- String()             // Initialized to empty string
  count as Integer: Integer()  // Explicit type with initializer

CONSTRUCTOR INITIALIZATION

Constructors can initialize fields using ':' assignment:

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

DEFAULT CONSTRUCTOR PATTERN

Use 'default ClassName()' to generate a no-arg constructor that initializes all fields to their defaults.

UNSET FIELDS

Fields declared with '?' are initially unset but exist:

  name as String?

They must still be declared with a type.

ALL FIELDS MUST EXIST

Every field must be declared in the class body. The compiler verifies that every field has a type and can be initialized.

See Q632 for define-before-use. See Q688 for variable init order. See Q563 for pure constructor assignment.

Example

defines module qa.dataflow.fieldinit

  defines class

    <?-
      Inline initialization: fields get default values at declaration.
      This is the simplest and most common pattern.
    -?>
    Configuration
      hostName <- "localhost"
      portNumber <- 8080
      isEnabled <- true

      getHostName() as pure
        <- rtn as String: hostName

      getPortNumber() as pure
        <- rtn as Integer: portNumber

      getIsEnabled() as pure
        <- rtn as Boolean: isEnabled

      default operator ?

    <?-
      Constructor initialization: fields set from parameters.
      Each field assigned in the constructor body.
    -?>
    UserProfile
      userName <- String()
      emailAddress <- String()
      loginCount <- Integer()

      UserProfile()
        ->
          userName as String
          emailAddress as String
        this.userName: userName
        this.emailAddress: emailAddress

      getUserName() as pure
        <- rtn as String: userName

      getEmailAddress() as pure
        <- rtn as String: emailAddress

      incrementLogin()
        loginCount: loginCount + 1

      default operator ?

    <?-
      Unset field pattern. A '?' field (query) must still be initialised by
      every public construction route: the constructor below sets it.
      A field assigned only later (resultCount, via setResultCount) is given a
      declaration initialiser to a present-but-unset value, so every route
      leaves it initialised; it reads as unset until setResultCount is called.
    -?>
    SearchResult
      query as String?
      resultCount as Integer: Integer()

      default private SearchResult()

      SearchResult()
        -> query as String
        this.query: query

      setResultCount()
        -> resultCount as Integer
        this.resultCount: resultCount

      describe()
        <- rtn as String: ""
        if query?
          if resultCount?
            rtn: `${query}: ${resultCount} results`
          else
            rtn: `${query}: pending`

      default operator ?

  defines program

    FieldInitializationDemo()
      stdout <- Stdout()

      config <- Configuration()
      stdout.println(`Host: ${config.getHostName()}:${config.getPortNumber()}`)

      profile <- UserProfile("alice", "alice@example.com")
      stdout.println(`User: ${profile.getUserName()}`)

      search <- SearchResult("ek9 tutorials")
      stdout.println(search.describe())
      search.setResultCount(42)
      stdout.println(search.describe())

Common mistakes

E08180 — All class fields must be initialized either inline at declaration or through constructors. Declaring fields without initialization or a default leaves them in an undefined state. See ek9 -h E08180 for details.

Incorrect:

hostName as String
      portNumber as Integer
      isEnabled as Boolean

Correct:

hostName <- "localhost"
      portNumber <- 8080
      isEnabled <- true
Other ways to ask this
  • What is E08180 field not initialized?
  • Must all class fields be initialized in EK9?
  • What happens if a field is not initialized in the constructor?
  • How do I initialize fields in EK9 classes?

Coming from another language?

Java: fields have default values (null, 0, false). Python: fields set in __init__, no compile-time check. C++: fields uninitialized unless in initializer list. Rust: all fields must be initialized. Go: fields zero-initialized. EK9: all fields must be explicitly initialized or declared with default.

Keywords: initialize, default, data-flow, E08180, field, declaration, safety, initialization, class, constructor, inline