How do field initialization and constructor delegation interact?

← Constructor Delegation · Ref: Q585

A field in EK9 is either initialized inline at declaration, or declared 'as T?' (no value) and then set by every public constructor. Constructors can also reassign inline-initialised fields using ':' or ':=?'. Understanding the interaction between field defaults and constructor assignment is essential.

FIELD DEFAULTS

Give a field an inline default:

  name <- String()    //initialized to unset String
  count <- 0          //initialized to 0

The alternative is 'name as String?' with no value, which needs the default constructor made private and every public constructor to set it (see Q886). A plain 'name as String' with no '?', no initialiser and no injection '!' is E08180.

CONSTRUCTOR ASSIGNMENT

Constructors use ':' to reassign fields after defaults are applied:

  MyClass()
    -> name as String
    this.name: name  //reassigns from default to parameter

ORDER OF INITIALIZATION

Field defaults are applied when the object is created. Then the constructor body runs. If delegation is used, the delegated constructor runs first.

COMBINING WITH DELEGATION

When using this() or super(), field defaults apply first, then the delegated constructor runs, then remaining statements in the calling constructor.

See Q580 for this() delegation. See Q581 for super() delegation. See Q104 for uninitialized properties. See Q563 for pure constructor field assignment.

Example

defines module qa.constructor.fieldinit

  defines class

    //Fields with defaults, constructors reassign
    Configuration
      hostname <- "localhost"
      portNumber <- 8080
      maxRetries <- 3

      //One-arg: only changes hostname
      Configuration()
        -> hostname as String
        this.hostname: hostname

      //Two-arg: changes hostname and port
      Configuration()
        ->
          hostname as String
          portNumber as Integer
        this.hostname: hostname
        this.portNumber: portNumber

      //Three-arg: changes all fields
      Configuration()
        ->
          hostname as String
          portNumber as Integer
          maxRetries as Integer
        this.hostname: hostname
        this.portNumber: portNumber
        this.maxRetries: maxRetries

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

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

      maxRetries() as pure
        <- rtn as Integer: maxRetries

      default operator ?

  defines program

    FieldInitDelegationDemo()
      stdout <- Stdout()

      //One-arg: port and retries keep defaults
      cfg1 <- Configuration("api.example.com")
      stdout.println(`${cfg1.hostname()}: ${cfg1.portNumber()}, retries ${cfg1.maxRetries()}`)

      //Two-arg: retries keeps default
      cfg2 <- Configuration("db.example.com", 5432)
      stdout.println(`${cfg2.hostname()}: ${cfg2.portNumber()}, retries ${cfg2.maxRetries()}`)

      //Three-arg: all custom
      cfg3 <- Configuration("cache.example.com", 6379, 5)
      stdout.println(`${cfg3.hostname()}: ${cfg3.portNumber()}, retries ${cfg3.maxRetries()}`)

Common mistakes

E04050 — A property initialiser cannot be another property or a complex expression - this is E04050 even when the referenced property is declared first. Initialise with a literal or a constructor call, and derive values from other properties inside a constructor. See ek9 -h E04050 for details.

Incorrect:

hostname <- portNumber
      portNumber <- 8080

Correct:

hostname <- "localhost"
      portNumber <- 8080
      maxRetries <- 3
Other ways to ask this
  • When should I use field defaults vs constructor assignment?
  • How do inline field values work with constructors?
  • What is the field initialization order in EK9?

Coming from another language?

Java: fields can be uninitialized (get default zero/null values). Python: fields set in __init__. Kotlin: properties initialized in init block or primary constructor. EK9: a field is initialized inline or declared 'as T?' and set by every public constructor; E08180 for a plain 'as T' with neither; constructors reassign with ':'.

Keywords: super, field, E08180, delegation, default, order, inline, constant, constructor, initialization, this