Why can't my class construct an object of itself through its private default constructor?

← Constructor Delegation · Ref: Q1404

A private constructor may leave properties without a value - the 'default private Label()' idiom - only because it is meant to be reached through this() from a constructor that then sets them. Any other way of reaching it, such as the type constructing an object of itself with 'Label()' in a method, would build a Label whose 'text' has no value, so the first read of it (or the generated '?') would fail at run time. EK9 rejects that call with E08264.

JUDGED THROUGH THE this() CHAIN
Each constructor is judged on what it leaves set: its own body plus, through this(...), whatever the constructor it delegates to sets. A 'default private X()' that sets nothing gives nothing, so a public constructor that calls this() must set every property itself (otherwise E08060 is reported at the property). After this(...) only what the delegate set has a value: reading any other property, or calling a method of the object, before setting it is E08060 or E08061.

FIX

Construct through a constructor that sets every property: pass the values ('Label("")'), or write a private constructor that sets them all. Such a constructor may be used anywhere in the type ('restarted()' below), and after this(...) to it the object is complete.

See Q886 for why the default constructor must be private. See Q580 for this() delegation. See Q1396 for using an object before it is constructed.

Example

defines module qa.constructordelegation.incompleteprivate

  defines class

    <?-
      The private default sets nothing. It may only be reached through this() from a constructor
      that then sets 'text' - never to construct a Label directly.
    -?>
    Label
      text as String?

      default private Label()

      Label()
        -> initial as String
        this()
        text: initial

      blank()
        <- rtn as Label: Label("")

      contents()
        <- rtn as String: text

      default operator ?

    <?-
      A private constructor that sets every property may be used anywhere in the type,
      and after this(...) to it what it set can be used straight away.
    -?>
    Tally
      total as Integer?

      default private Tally()

      private Tally()
        -> start as Integer
        total: start

      Tally()
        ->
          start as Integer
          extra as Integer
        this(start)
        total += extra

      restarted()
        <- rtn as Tally: Tally(0)

      current()
        <- rtn as Integer: total

      default operator ?

  defines program

    IncompletePrivateConstructorDemo()
      stdout <- Stdout()

      named <- Label("named")
      cleared <- named.blank()
      stdout.println(`${named.contents()} [${cleared.contents()}] ${cleared?}`)

      tallied <- Tally(2, 3)
      stdout.println(`${tallied.current()} ${tallied.restarted().current()}`)

Common mistakes

E08264 — 'Label()' is the private default, which sets nothing - it may only be reached through this() from a constructor that then sets 'text'. Construct through a constructor that sets every property. See ek9 -h E08264 for details.

Incorrect:

Label()

Correct:

Label("")
Other ways to ask this
  • What is E08264 CONSTRUCTOR_LEAVES_PROPERTIES_UNSET?
  • Why is calling my 'default private' constructor from a method an error?
  • Does this() to an empty private constructor initialise my properties?
  • Why does a constructor that delegates with this() still have to set every property?

Coming from another language?

Java requires a final field to be assigned by every constructor, directly or through this(...), but a non-final field may be left null by any constructor - including a private one that a factory method calls. Kotlin and Swift require every non-optional property to be initialised by every constructor or initialiser. EK9 applies the rule to every property declared without a value, follows this(...) chains, and lets a private constructor leave properties unset only when it is reached through this().

Keywords: unset, E08264, default, uninitialised, factory, private, constructor, property, delegation