How can an object hold a reference to itself, like a doubly linked node that starts as a ring of one?
← Classes and OOP · Ref: Q1471
A property of the object's own type may refer back to the object: assign 'this' to it in the constructor, with ':', ':=', '=' or ':=?' ('prev: this', 'this.owner :=? this'). Storing the object in one of its OWN properties is not a use of it - nothing is read through it and nothing outside the object holds it - so it sets that property like any other assignment, even while other properties are still unset. A doubly linked node can start as a ring of one before its label is set:
Node
prev as Node?
next as Node?
label as String?
default private Node()
Node()
-> text as String
prev: this
next: this
label: text
label()
<- rtn as String: label
override operator ? as pure <- rtn as Boolean: label?
THE PROPERTY IS THE OBJECT UNDER ANOTHER NAME
Only 'this' itself may be stored that way, and in that constructor the property then IS the object. Calling a method through it ('itself.doubled()'), copying it to a local, or reading 'this.itself' is a use of the object, so it needs every property set first, exactly as a method call on 'this' does (E08061; see Q1396). Passing it to another object's method is publishing a half-built object (E08250), as passing 'this' is. Once every property is set it can be used freely. The same store one step removed - a child created with a capture of 'this' and kept in the object's own property ('section :=? (owner: this) with trait of Part as class ...') - is the parent link: see Q2181.
GENERATED OPERATORS ARE SAFE ON A CYCLE
A type like this can reach itself through its properties, and the compiler works that out. Its GENERATED operators ('default operator ?', '$', '$$', '==', '#?') then carry a cycle guard, so a property that refers back never recurses without end: '?' treats the object it is already checking as set; '$' and '$$' on a cycle are unset (never JSON with a hole where the loop was); '==' assumes a pair it is already comparing is equal, so two identical rings are equal; and '#?' leaves out the properties that lead back. A type that cannot reach itself pays nothing. The node above still writes its own '?', because only its label says whether it is set.
Example
defines module qa.oop.property.refers.to.itself defines class Node prev as Node? next as Node? label as String? default private Node() Node() -> text as String prev: this next: this label: text label() <- rtn as String: label next() <- rtn as Node: next prev() <- rtn as Node: prev linkAfter() -> node as Node follower <- next node.joinBetween(this, follower) follower.joinPrev(node) next: node joinBetween() -> before as Node after as Node prev: before next: after joinPrev() -> node as Node prev: node override operator ? as pure <- rtn as Boolean: label? Tally itself as Tally? count as Integer? total as Integer: 0 default private Tally() Tally() -> start as Integer itself: this count: start total: itself.doubled() doubled() <- rtn as Integer: count * 2 value() <- rtn as Integer: total override operator ? as pure <- rtn as Boolean: total? defines program SelfReferenceDemo() stdout <- Stdout() first <- Node("first") stdout.println(`after first: ${first.next().label()}`) stdout.println(`before first: ${first.prev().label()}`) first.linkAfter(Node("second")) second <- first.next() stdout.println(`ring: ${first.label()}, ${second.label()}, ${second.next().label()}`) stdout.println(`back: ${first.prev().label()}`) stdout.println($Tally(21).value())
Common mistakes
E08061 — 'itself' was set to 'this', so calling doubled() through it is a call on the object - and doubled() reads 'count', which is not set yet. Storing 'this' is fine at any point; reading through it waits until every property is set.
Incorrect:
total: itself.doubled() count: start
Correct:
count: start total: itself.doubled()
Other ways to ask this
- Can a constructor assign 'this' to one of the object's own properties?
- How do I write 'me: this' in an EK9 constructor without E08061?
- How do I build a circular linked list node whose next and previous point to itself?
- Why is reading through a property set to 'this' in the constructor still E08061?
Coming from another language?
Java: 'this.next = this;' in a constructor compiles, and so does every use of a half-built object - the compiler checks neither. Swift: two-phase initialisation - 'self' cannot be used until every stored property is set, so a property that must point at 'self' is declared optional or implicitly unwrapped and set after. Kotlin: 'lateinit var next: Node' and 'next = this' in init, with a 'Leaking this' warning on any other use. EK9: storing 'this' in an own property is allowed at any point in the constructor; any read through that property waits for the object to be complete (E08061).
Keywords: property, doubly linked, circular, node, self reference, linked list, constructor, E08250, E08061, self-referential, ring