Can a generic class have multiple constructors in EK9, and do they all have to be public to avoid E06060?

← Generics · Ref: Q1287

Generic classes in EK9 can have as many constructors as they need — overloading, default, parameterised. The access rule is nuanced, not absolute: the PARAMETERISED (inferred) constructors must be public so the compiler can infer type arguments and instantiate from any context; the no-argument DEFAULT constructor may additionally be 'default private' as an escape hatch; and PROTECTED is never allowed on any generic constructor.

  Error : E06060: 'Box' on line 15 position 22: a generic type does not support private or protected constructors

E06060 fires for a PROTECTED constructor (any), or for a PRIVATE PARAMETERISED constructor. It does NOT fire for a 'default private' no-arg constructor.

CORRECT: MULTIPLE PUBLIC CONSTRUCTORS

  defines class
    Box of type T
      item as T?
      label as String?
      Box() as pure
        item :=? T()
        label :=? String()
      Box() as pure
        -> initialItem as T
        item :=? initialItem
        label :=? String()
      Box() as pure
        ->
          initialItem as T
          tag as String
        item :=? initialItem
        label :=? tag

Three constructors: no-argument, single-argument, two-argument. All public. All allowed. The no-arg constructor gives the conceptual field an unset 'T()' so it is present-but-unset, never null.

CORRECT: PRIVATE NO-ARG DEFAULT (the escape hatch)

  defines class
    Box of type T
      item as T?
      default private Box() as pure           // allowed: the no-arg default may be private
      Box() as pure
        -> initialItem as T
        item :=? initialItem

When the field cannot be given an unset 'T()' (T may be abstract, may hold a function, or may have no public no-arg constructor), make the no-arg default private. External code then cannot create an empty Box; it must pass a value to the inferred constructor.

INCORRECT: PRIVATE OR PROTECTED PARAMETERISED CONSTRUCTOR

  defines class
    Box of type T
      item as T?
      Box() as pure
        item :=? T()
      private Box() as pure                    // E06060 — a parameterised constructor must be public
        -> initialItem as T
        item :=? initialItem

Type inference needs the parameterised constructor public, so making it private (or protected) is E06060.

HIDING VALIDATED CONSTRUCTION: A FACTORY FUNCTION

To route callers through validation, keep the public parameterised constructors and add a factory function in the same module:

  defines function
    createValidatedBox() as pure
      -> value as String
      <- rtn as Box of String?
      if value?
        rtn: Box(value)

Use 'default private' to forbid EMPTY construction; use a factory to control VALUED construction with validation, defaulting, or logging.

See Q1286 for the single-constructor form. See Q645 for the private no-arg escape hatch.

Example

defines module qa.genericsdeep.e06060multi

  defines class

    Box of type (T, U)
      first as T?
      second as U?

      Box() as pure
        first :=? T()
        second :=? U()

      Box() as pure
        ->
          firstValue as T
          secondValue as U
        first :=? firstValue
        second :=? secondValue

      getFirst() as pure
        <- rtn as T: first

      getSecond() as pure
        <- rtn as U: second

      override operator ? as pure
        <- rtn as Boolean: first? and second?

      operator $ as pure
        <- rtn as String: `(${first}, ${second})`

  defines program

    E06060MultiDemo()
      stdout <- Stdout()

      labelled <- Box(42, "answer")
      stdout.println($labelled)

      keyed <- Box("alpha", 3.14)
      stdout.println($keyed)

Common mistakes

E06060 — Generic types reject both 'private' and 'protected' constructor modifiers absolutely; a 'protected' constructor on a generic type fires E06060. See ek9 -h E06060 for details.

Incorrect:

      protected Box() as pure
        first :=? T()

Correct:

      Box() as pure
        first :=? T()
Other ways to ask this
  • How do I write a generic class with more than one constructor in EK9?
  • Does E06060 block protected constructors as well as private ones on generic types?
  • Can I have an overloaded generic constructor where some are public and some are private?
  • Why does my generic class with a default constructor and a parameterised constructor fail E06060?

Coming from another language?

C#/Java allow private constructors on generic types, commonly paired with static factory methods inside the same class for controlled construction. EK9 takes a different position: generic constructors are public by rule, and factory functions live alongside the generic in the same module. This keeps monomorphisation simple and the code-generation stage deterministic.

Keywords: multiple constructors, E06060, monomorphisation, generic type, constructor overloading, public constructor