What are the complete constructor rules for generic types?
← Generics · Ref: Q683
Generic types have specific constructor requirements to support type inference, default instantiation, and external usage.
TWO CONSTRUCTORS REQUIRED (E06040)
Every generic type must have a no-argument constructor and an inferred (typed) constructor. A conceptual-T property declared 'item as T?' must NOT be left null by the no-arg constructor - give it an unset 'T()' so it is present-but-unset, never null:
Container of type T item as T? Container() as pure item :=? T()
PARAMETER TYPE MATCHING (E06050)
Constructor parameters must use the declared type parameters:
Box of type T Box(item as T) // Correct: parameter is T Box(item as String) // Wrong: must use T
ACCESS (E06060)
The inferred (parameterised) constructor must be public for type inference. The no-arg DEFAULT constructor may be 'default private' as an escape hatch when the field cannot be given an unset 'T()' (T abstract, holds a function, or has no public no-arg constructor); external code then cannot create an empty instance. Protected is never allowed.
PURE CONSTRUCTOR CONSISTENCY
If the default constructor is pure, all other constructors must also be pure (E05190). In a pure constructor ':=' is forbidden, so use the guarded ':=?' to first-initialise the field.
COMBINED PATTERN
Container of type T item as T? Container() as pure item :=? T() // optional field, never null Container(item as T) as pure // inferred, public this.item :=? item
See Q642 for constructor inference. See Q643 for two-constructor requirement. See Q645 for the private no-arg escape hatch.
Example
defines module qa.genericsdeep.constructorrules defines class <?- Correct generic type with all constructor rules satisfied. 1. Default constructor present (public, pure) 2. Typed constructor uses type parameter T 3. All constructors are pure (consistency) -?> Container of type T element as T? //Rule 1: default no-arg constructor required Container() as pure element :=? T() //Rule 2: typed constructor uses T (not a concrete type) Container() as pure -> item as T element :=? item //Public methods for access getElement() <- rtn as T: T() rtn :=? T(element) isEmpty() as pure <- rtn as Boolean: not element? default operator ? <?- Multi-parameter generic with correct constructors. Both type parameters used in typed constructor. -?> Association of type (K, V) theKey as K? theValue as V? //Default no-arg constructor Association() as pure theKey :=? K() theValue :=? V() //Typed constructor uses both K and V Association() as pure -> theKey as K theValue as V this.theKey :=? theKey this.theValue :=? theValue key() <- rtn as K: K() rtn :=? K(theKey) getTheValue() <- rtn as V: V() rtn :=? V(theValue) default operator ? defines program GenericConstructorRulesDemo() stdout <- Stdout() //Type inference from argument intContainer <- Container(42) stdout.println(`Container set: ${intContainer?}`) //Explicit type parameter emptyContainer <- Container() of String stdout.println(`Empty container: ${emptyContainer.isEmpty()}`) //Multi-parameter inference assoc <- Association("key", 99) stdout.println(`Association set: ${assoc?}`)
Common mistakes
E06050 — Constructor parameters in generic types must use the declared type parameter T, not a concrete type like String. Using a concrete type triggers E06050. See ek9 -h E06050 for details.
Incorrect:
Container() as pure -> item as String element :=? item
Correct:
Container() as pure -> item as T element :=? item
E06040 — Every generic type needs two constructors, a default no-argument one and an inferred typed one; omitting the no-arg constructor triggers this. See ek9 -h E06040 for details.
Incorrect:
Container() as pure -> item as T
Correct:
Container() as pure element :=? T() //Rule 2: typed constructor uses T (not a concrete type) Container() as pure -> item as T
E50060 — Container has no toString() method. Use the $ prefix operator or ? for boolean check. See ek9 -h E50060 for details.
Incorrect:
stdout.println(intContainer.toString())
Correct:
stdout.println(`Container set: ${intContainer?}`)
Other ways to ask this
- What is E06040 generic constructor argument type mismatch?
- What is E06050 generic default constructor required?
- What is E06060 generic constructor must be public?
- What constructors does a generic type need?
Coming from another language?
Java: generic classes need no special constructor rules. Rust: no constructors, uses associated functions. Go: no constructors, uses factory functions. Kotlin: generic classes follow standard constructor rules. EK9: generic types require default public constructor, typed constructor params must match type parameters.
Keywords: type, side-effect, generic, E06060, constant, type-parameter, parameter, E06050, immutable, constructor, default, pure, E06040