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 more than one constructor, but the set is fixed: a no-argument constructor and the PARAMETERISED (inferred) constructor, whose number of parameters equals the number of type parameters (anything else is E06030). The access rule is nuanced, not absolute: the parameterised constructor must be public so the compiler can infer type arguments and instantiate from any context; the no-argument constructor may be private (either 'default private' or a hand-written 'private' one with a body) 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 private no-arg constructor.
CORRECT: TWO 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()
default operator ?
Two constructors: no-argument and single-argument (one type parameter, one argument). Both public. Adding a third, two-argument constructor such as (initialItem as T, tag as String) is E06030 - for type inference to work the number of generic and constructor parameters must be the same; set the label through a method or a factory function instead. 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
default operator ?
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 -> candidate as String <- rtn as Box of String: Box() of String if candidate? rtn: Box(candidate)
The return value starts as an empty Box so it is set on every path (an uninitialised 'Box of String?' set only inside the if is E08050).
Use a private no-arg constructor to forbid EMPTY construction; use a factory to control VALUED construction with validation, defaulting, or logging.
See Q1286 for the single-constructor form and 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 — 'protected' is never allowed on a generic type's constructor, so it fires E06060. 'private' is rejected only on a parameterised constructor; any no-arg constructor may be private. 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: a generic's parameterised constructor is public by rule (only the no-arg constructor may be private, and protected is never allowed), and factory functions live alongside the generic in the same module. This keeps monomorphisation simple and the code-generation stage deterministic.
Keywords: monomorphisation, E06060, multiple constructors, public constructor, constructor overloading, generic type