How do I implement a constructor in EK9?
← Classes and OOP · Ref: Q94
Constructors in EK9 share the name of the class. You can define multiple constructors with different parameter lists. Properties are initialised using the colon assignment operator.
BASIC CONSTRUCTOR
A constructor takes parameters with '->' and assigns to properties:
Account()
-> holder as String
this.holder: holder
The 'this.' prefix disambiguates property from parameter when names match.
PROPERTY INITIALIZATION
Properties can have default values or be set in the constructor:
balance <- 0.0 default value at declaration holder <- String() starts unset, must be set in constructor
MULTIPLE CONSTRUCTORS
Overload constructors with different parameter lists:
Account()
-> holder as String
this(holder, 0.0)
Account()
->
holder as String
initialBalance as Float
this.holder: holder
this.balance: initialBalance
Multiple parameters use multi-line '->' blocks (comma-separated on one line is not valid). Use 'this(...)' to chain to another constructor.
COPY CONSTRUCTOR
The copy operator ':=:' copies all fields from another instance:
operator :=: -> from as Account holder :=: from.holder balance :=: from.balance
DEFAULT CONSTRUCTOR
Use 'default' keyword to hide or control the no-arg constructor:
default private Account()
This prevents external creation without parameters.
PURE CONSTRUCTORS
Mark a constructor 'as pure' to enforce immutability rules. Pure constructors use ':=?' (guarded assign) instead of ':' for field assignment. Critical rule: if ANY constructor is pure, ALL constructors must be pure (E05190).
ACCESS MODIFIERS
Constructors are public by default. Do NOT write 'public Account()' — the compiler rejects redundant 'public' (E07280). Use 'private' or 'protected' only when restricting access.
DYNAMIC CLASS CONSTRUCTORS
Named dynamic classes can define explicit constructors for reusable types:
entry <- PersonRecord(name: n, age: a) as class PersonRecord() -> name as String age as Integer this.name: name this.age: age default operator ?
The captured variables become fields, and custom constructors control the interface.
See Q93 for class basics. See Q95 for field visibility. See Q104 for uninitialised properties. See Q98 for record operators. See Q118 for builder pattern. See Q580 for constructor delegation patterns. See Q563 for pure constructor assignment rules. See Q115 for dynamic classes. See Q598 for why EK9 has no default parameters and uses constructor overloading instead.
Example
defines module qa.oop.constructors defines class Account holder <- String() balance <- 0.0 default private Account() Account() -> holder as String this(holder, 0.0) Account() -> holder as String initialBalance as Float this.holder: holder this.balance: initialBalance holder() as pure <- rtn as String: holder balance() as pure <- rtn as Float: balance deposit() -> amount as Float balance += amount operator :=: -> from as Account holder :=: from.holder balance :=: from.balance default operator ? defines program ConstructorDemo() stdout <- Stdout() // === BASIC CONSTRUCTOR === account1 <- Account("Alice") stdout.println(`Holder: ${account1.holder()}, Balance: ${account1.balance()}`) // === MULTIPLE CONSTRUCTORS === account2 <- Account("Bob", 100.0) stdout.println(`Holder: ${account2.holder()}, Balance: ${account2.balance()}`) // === CONSTRUCTOR CHAINING === account1.deposit(50.0) stdout.println(`After deposit: ${account1.balance()}`) // === COPY OPERATOR === account3 <- Account("Temp") account3 :=: account2 stdout.println(`Copied: ${account3.holder()}, Balance: ${account3.balance()}`)
Common mistakes
E08180 — A bare 'holder as String' with no initializer and no injection marker '!' triggers E08180. Use '<-' with a default value, explicit type with ':' initializer, or 'as String?' for an unset field. See ek9 -h E08180 for details.
Incorrect:
holder as String
Correct:
holder <- String()
E07280 — Constructors and methods are public by default in EK9. Adding an explicit 'public' modifier is redundant and triggers E07280. Simply omit it. Use 'private' or 'protected' only when restricting access. See ek9 -h E07280 for details.
Incorrect:
public Account() -> holder as String
Correct:
Account()
-> holder as String
E08130 — If ANY constructor is marked 'as pure', ALL constructors must be pure. Mixing pure and non-pure constructors triggers E05190. Also note pure constructors use ':=?' not ':' for field assignment. See ek9 -h E05190 for details.
Incorrect:
Account() as pure -> holder as String this(holder, 0.0)
Correct:
Account()
-> holder as String
this(holder, 0.0)
Other ways to ask this
- What is the syntax for EK9 class constructors?
- How do constructors work in EK9 classes?
- Can I have multiple constructors in an EK9 class?
Coming from another language?
Java: constructors match class name, this() chaining, no named parameters, copy via clone() or copy constructor. Python: __init__(self) only, no overloading (use defaults), copy via copy.deepcopy(). Rust: no constructors, use associated functions like new(), From trait for conversion. Go: no constructors, convention is NewType() factory functions. Kotlin: primary constructor in class header, secondary with constructor keyword, copy() on data classes. EK9: constructors match class name, multiple overloads, this() chaining, ':=:' copy operator, default private constructors.
Keywords: overload, default, copy, constructor, initialise, property, object-oriented, private, parameter, chain, this, constant