How do field initialization and constructor delegation interact?
← Constructor Delegation · Ref: Q585
Fields in EK9 must be initialized inline at declaration. Constructors then optionally reassign fields using ':' or ':=?'. Understanding the interaction between field defaults and constructor assignment is essential.
FIELD DEFAULTS
Every field must have an inline default:
name <- String() //initialized to unset String count <- 0 //initialized to 0
Fields cannot be left uninitialized (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 — Using a field before it has been declared is a forward reference error. Fields must be declared before being referenced. 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: fields must be initialized inline, E08180 if not, constructors reassign with ':'.
Keywords: order, default, delegation, E08180, field, super, this, initialization, constructor, constant, inline