Can a generic type constructor be private?
← Generics · Ref: Q645
EK9 generic type constructors are public, with ONE exception: the no-argument DEFAULT constructor may be declared 'default private'. This is the escape hatch for a generic that has an uninitialised conceptual-T property it cannot give an unset value to - privacy then stops external code creating a partially-constructed (null-field) instance, while the inferred constructor still supplies a real value.
THE PUBLIC RULE
The inferred (parameterised) constructor must be public so the compiler can infer type arguments and instantiate from any context:
Container of type T item as T? Container() as pure item :=? T() // give the optional field an unset T - never null Container(val as T) as pure // inferred constructor: must be public item :=? val
THE PRIVATE NO-ARG ESCAPE HATCH
When a generic cannot give its field an unset 'T()' (T may be abstract, may hold a function, or may lack a public no-arg constructor), declare the default private:
default private Container() as pure
External code then cannot create an empty Container; it must pass a value to the inferred constructor.
STILL NOT ALLOWED
1. A private or protected PARAMETERISED constructor - type inference needs it public.
2. A protected no-arg default - it would still allow subclass uninitialised construction.
See Q642 for constructor inference. See Q643 for two-constructor requirement. See Q194 for generic class basics.
See Q682 for generic encapsulation. See Q683 for generic constructor rules.
Example
defines module qa.genericsdeep.publicconstructor defines class <?- Correct: both constructors are public (default access). Generic types require public constructors for inference. -?> Cache of type T stored as T? Cache() as pure stored :=? T() Cache() as pure -> initial as T stored :=? initial retrieve() <- rtn as T: T() rtn :=? T(stored) default operator ? <?- Correct: using 'default' keyword generates public constructor. -?> Slot of type T item as T? Slot() as pure item :=? T() Slot() as pure -> initialValue as T item :=? initialValue default operator ? defines program PublicConstructorDemo() stdout <- Stdout() // === PUBLIC CONSTRUCTORS ENABLE INFERENCE === intCache <- Cache(42) stdout.println(`Cache set: ${intCache?}`) strSlot <- Slot("data") stdout.println(`Slot set: ${strSlot?}`) // === PUBLIC DEFAULT ENABLES EXPLICIT TYPING === emptyCache <- Cache() of Float stdout.println(`Empty cache set: ${emptyCache?}`)
Common mistakes
E06060 — Generic type constructors must be public. A private constructor would prevent type inference and instantiation from external code. The compiler needs unrestricted access to constructors for generic type instantiation. See ek9 -h E06060 for details.
Incorrect:
private Cache() as pure -> initial as T stored :=? initial
Correct:
Cache() as pure -> initial as T stored :=? initial
Other ways to ask this
- What is E06060 generic constructors must be public?
- Can I use private constructors in a generic class?
- When is a private constructor allowed on a generic type?
Coming from another language?
Java: generic classes can have private constructors (factory pattern). C++: templates can have private constructors. Rust: no constructor keyword, but 'new' can be private. Go: unexported functions serve as private constructors. EK9: generic constructors must be public for type inference and external instantiation.
Keywords: constructor, access, private, instantiation, constant, visibility, type-parameter, public, generic, E06060