Why can't a property initialiser use 'this', not even 'this.width'?
← Classes and OOP · Ref: Q2180
A property initialiser may not use 'this' at all - not as a value, not in a capture list, and not to reach a property ('this.width'). Write the property's plain name instead: 'height as Integer: width * 2'.
WHY: TWO-PHASE CONSTRUCTION
Building an object has two phases. Phase one gives every property a value: the super constructor runs, then this type's property initialisers in declaration order, then the constructor body. Phase two begins only when EVERY property has a value - the object is complete - and only then may anything use it: call its methods, hand it out, let something else hold it. A property initialiser always runs in phase one, before the constructor body, so the object is NEVER complete while an initialiser runs. 'this' there is a half-built object: a method called through it, or a dynamic function holding it, could reach a property that has not been given its value yet (in Java that compiles and fails with a NullPointerException, or sees a 0 that is not the real value).
WHAT AN INITIALISER MAY READ
Only a property declared EARLIER that has its own initialiser - that initialiser has already run (anything else is E08060). Read it by its plain name: 'height as Integer: width * 2'. A capture list in an initialiser is evaluated by the initialiser, so it follows the same rule: '(factor: width)' captures the earlier property's object, while '(owner: this)' would capture the half-built object (E08061). A value that needs more comes from a function ('depth as Integer: defaultDepth()') or from a constructor argument ('label: name' in the constructor).
WHY NOT ALLOW 'this.width', WHICH READS THE SAME FIELD?
Because one rule with no exceptions is easier to see and to keep - for people and for AI - than a list of allowed uses. Once 'this' may appear in an initialiser, the next edit is 'this.describe()', '$this' or '(owner: this)', each of which uses the half-built object. So the rule is simply: no 'this' in an initialiser, and E08061 tells you to write 'width'.
When the object needs a reference to itself - a child that points back to its parent - build it in the CONSTRUCTOR, where EK9 allows it in one exact shape: see Q2181. For calling the object's own methods during construction see Q1396; for the order initialisers run in see Q1397.
Example
defines module qa.oop.no.this.in.initialiser defines function Scaler() as abstract -> amount as Integer <- rtn as Integer? defaultDepth() <- rtn as Integer: 4 defines class Box width as Integer: 3 height as Integer: width * 2 depth as Integer: defaultDepth() scale as Scaler: (factor: width) is Scaler as function (rtn: amount * factor) label as String? default private Box() Box() -> name as String label: name width() as pure <- rtn as Integer: width volume() <- rtn as Integer: width * height * depth scaled() -> amount as Integer <- rtn as Integer: scale(amount) describe() <- rtn as String: `${label}: ${width} x ${height} x ${depth}` default operator ? defines program NoThisInInitialiser() stdout <- Stdout() box <- Box("crate") stdout.println(box.describe()) stdout.println(`volume: ${box.volume()}`) stdout.println(`scaled 10: ${box.scaled(10)}`) stdout.println(`width: ${box.width()}`)
Common mistakes
E08061 — A property initialiser runs before the constructor, while the object is still being built, so it may not use 'this' at all - even to reach a property. Write the property's plain name: 'width * 2' reads the earlier, already-initialised 'width'.
Incorrect:
height as Integer: this.width * 2
Correct:
height as Integer: width * 2
E08061 — The capture list is evaluated by the initialiser, so '(owner: this)' would hand the half-built object to the function. Capture the earlier property itself, '(factor: width)', or build the function in the constructor once the object is complete.
Incorrect:
(owner: this) is Scaler as function (rtn: amount * owner.width())
Correct:
(factor: width) is Scaler as function (rtn: amount * factor)
Other ways to ask this
- Why is 'this.width' in a property initialiser E08061?
- Can I use this in a property initialiser in EK9?
- Can a property initialiser capture 'this' in a dynamic function?
- What is two-phase construction in EK9?
- How does a property initialiser read another property of the same object?
Coming from another language?
Java: a field initialiser may use 'this' freely - pass it out, call overridable methods - and the bugs show up at run time as nulls or zeros. Kotlin: property initialisers may reference 'this'; leaking it is only a warning. Swift: a stored property's default value cannot reference 'self' at all (a compile error) - EK9 takes the same position and adds that the constructor's use of 'this' waits until every property is set. C#: a field initializer cannot use 'this' or read another instance field at all; EK9 is a little more lenient - an initialiser may read an EARLIER initialised property by its plain name - but never through 'this'.
Keywords: property, half-built, field, initializer, capture, initialiser, construction, constructor, E08060, E08061, two-phase