Why must a generic class have both a default and an inferred-type constructor?
← Generics · Ref: Q643
EK9 requires generic types to have exactly two constructors: a default (no-argument) constructor and an inferred-type constructor whose parameters match the type parameters.
TWO CONSTRUCTOR RULE
Generic types need:
1. A default constructor: 'default MyType()' for creating unset instances
2. An inferred constructor: 'MyType(val as T)' for type-inferred creation
WHY BOTH ARE NEEDED
The default constructor enables:
container <- Container() of String
Creates an instance when no initial value is available.
The inferred constructor enables:
container <- Container("hello")
Type parameter inferred from the argument.
DEFAULT KEYWORD
The 'default' keyword generates a no-argument constructor:
default Container() as pure
This creates a Container where T is unset.
See Q642 for constructor inference. See Q644 for constructor argument type matching. See Q194 for generic class basics.
Example
defines module qa.genericsdeep.twoconstructors defines class <?- Correct: generic class with both default and inferred constructors. Default constructor creates unset instance. Inferred constructor enables type inference. -?> Holder of type T stored as T? Holder() as pure stored :=? T() Holder() as pure -> initial as T stored :=? initial retrieve() <- rtn as T: T() rtn :=? T(stored) default operator ? <?- Another correct example with multi-parameter generic. Both default and inferred constructors present. -?> KeyVal of type (K, V) theKey as K? theValue as V? KeyVal() as pure theKey :=? K() theValue :=? V() KeyVal() as pure -> k as K v as V theKey :=? k theValue :=? v default operator ? defines program TwoConstructorDemo() stdout <- Stdout() // === DEFAULT CONSTRUCTION (needs explicit type) === emptyHolder <- Holder() of Integer stdout.println(`Empty holder set: ${emptyHolder?}`) // === INFERRED CONSTRUCTION === filledHolder <- Holder("data") stdout.println(`Filled holder set: ${filledHolder?}`) // === MULTI-PARAM DEFAULT === emptyKV <- KeyVal() of (String, Float) stdout.println(`Empty KV set: ${emptyKV?}`) // === MULTI-PARAM INFERRED === filledKV <- KeyVal("price", 9.99) stdout.println(`Filled KV set: ${filledKV?}`)
Common mistakes
E06040 — A generic type requires both a default (no-argument) constructor and an inferred-type constructor; deleting the no-arg constructor leaves only the inferred one and fails. See ek9 -h E06040 for details.
Incorrect:
Holder() as pure -> initial as T
Correct:
Holder() as pure stored :=? T() Holder() as pure -> initial as T
Other ways to ask this
- What is E06040 generic type requires two constructors?
- Why do I need two constructors for a generic class in EK9?
- What constructors does a generic type need in EK9?
Coming from another language?
Java: no constructor requirements for generics, relies on 'new T()' (impossible due to erasure). C++: default-constructible via 'requires std::default_initializable<T>'. Rust: Default trait optional. Go: zero-value initialization built in. Kotlin: no mandatory constructor count. EK9: two constructors required, default + inferred, enabling both patterns.
Keywords: default, instantiation, requirement, type-parameter, generic, two, inferred, class, constructor, constant, E06040