How do constructors work in multi-level class hierarchies?

← Constructor Delegation · Ref: Q587

In multi-level hierarchies, each level uses super() to initialize its immediate parent. Parameters flow from the most derived class through each parent in the chain.

THREE-LEVEL PATTERN

Grandparent defines base fields and constructor. Parent extends grandparent, adds fields, calls super() to grandparent. Child extends parent, adds fields, calls super() to parent. Each level only passes what its parent needs.

PARAMETER FLOW

Child receives all parameters and passes a subset to parent:

  Employee(name, age, role)
    super(name, age)  //Person gets name and age
  Person(name, age)
    super(name)        //Entity gets name
  Entity(name)
    this.name: name    //stores name

EACH LEVEL INITIALIZES ITS OWN STATE

Each constructor calls super() with the parent's parameters, then initializes its own fields. This keeps each class responsible for its own state.

DEFAULT CONSTRUCTORS IN THE CHAIN

If any level has a default constructor, the child does not need explicit super() for that level.

See Q580 for this() delegation. See Q581 for super() delegation. See Q582 for delegation order. See Q575 for deep override chains.

Example

defines module qa.constructor.hierarchy

  defines class

    //Level 1: base entity
    Entity as open
      entityName <- String()

      Entity()
        -> entityName as String
        this.entityName: entityName

      entityName() as pure
        <- rtn as String: entityName

      default operator ?

    //Level 2: adds age
    Person extends Entity as open
      personAge <- 0

      Person()
        ->
          entityName as String
          personAge as Integer
        super(entityName)
        this.personAge: personAge

      personAge() as pure
        <- rtn as Integer: personAge

      default operator ?

    //Level 3: adds department
    Employee extends Person
      department <- String()

      Employee()
        ->
          entityName as String
          personAge as Integer
          department as String
        super(entityName, personAge)
        this.department: department

      department() as pure
        <- rtn as String: department

      default operator ?

  defines program

    HierarchyConstructorsDemo()
      stdout <- Stdout()

      emp <- Employee("Alice", 30, "Engineering")
      stdout.println(`Name: ${emp.entityName()}`)
      stdout.println(`Age: ${emp.personAge()}`)
      stdout.println(`Dept: ${emp.department()}`)

      //Polymorphic use
      entities <- List() of Entity
      entities += Entity("Base")
      entities += Person("Bob", 25)
      entities += Employee("Charlie", 35, "Sales")

      for entity in entities
        stdout.println(`Entity: ${entity.entityName()}`)

Common mistakes

E05050 — super() must be the first statement in the constructor. The parent (Person) must be initialized before the child (Employee) assigns its own fields. See ek9 -h E05050 for details.

Incorrect:

Employee()
        ->
          entityName as String
          personAge as Integer
          department as String
        this.department: department
        super(entityName, personAge)

Correct:

Employee()
        ->
          entityName as String
          personAge as Integer
          department as String
        super(entityName, personAge)
        this.department: department

E50060 — String has no toUpperCase() method in EK9. Use upperCase() instead. See ek9 -h E50060 for details.

Incorrect:

stdout.println(emp.entityName().toUpperCase())

Correct:

stdout.println(`Name: ${emp.entityName()}`)
Other ways to ask this
  • How do I pass parameters through a three-level constructor chain?
  • How does super() work with grandparent constructors?
  • What is the constructor pattern for deep hierarchies?

Coming from another language?

Java: same pattern, super() in each level. Python: super().__init__() chains through MRO. Kotlin: primary constructor in class header handles delegation. C#: base() at each level. EK9: super() at each level, parameters flow through chain.

Keywords: grandparent, class, constructor, delegation, multi-level, super, parameter, chain, flow, this, hierarchy, constant