What does EK9 error E06060 mean on a generic class constructor and how do I fix it?

← Generics · Ref: Q1286

E06060 fires when a generic type (a class declared as 'Name of type T' or 'Name of type (T, U)') has a PROTECTED constructor, or a PRIVATE PARAMETERISED constructor. The compiler reports this at phase 1 with a message like:

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

The 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 be 'default private'. Protected is never allowed.

THE FIX (private parameterised constructor)
Make the parameterised constructor public — public is the default, so drop the access modifier:

  Container() as pure
    -> initialItem as T
    item :=? initialItem

GIVE THE OPTIONAL FIELD AN UNSET VALUE, NOT NULL

A conceptual field 'item as T?' must not be left null by the no-arg constructor — give it an unset 'T()':

  Container() as pure
    item :=? T()

THE 'default private' ESCAPE HATCH IS ALLOWED ON GENERICS
The 'default private Container() as pure' idiom from regular classes DOES work on a generic — use it 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). External code then cannot create an empty Container; it must pass a value:

  Container of type T
    item as T?
    default private Container() as pure       // allowed
    Container() as pure
      -> initialItem as T
      item :=? initialItem

WHY PARAMETERISED CONSTRUCTORS STAY PUBLIC

Generic types are reused with ANY valid type parameter; the compiler monomorphises each parameterisation (Container of String, Container of Integer, …). Type inference and instantiation need the parameterised constructor reachable from every call site, so it must be public. The no-arg default has no inference role, so it may be private.

See Q645 for the full rationale and the class-level rule comparison.

Example

defines module qa.genericsdeep.e06060fix

  defines class

    Container of type T
      item as T?

      Container() as pure
        item :=? T()

      Container() as pure
        -> initialItem as T
        item :=? initialItem

      getItem() as pure
        <- rtn as T: item

      override operator ? as pure
        <- rtn as Boolean: item?

      operator $ as pure
        <- rtn as String: $item

  defines program

    E06060FixDemo()
      stdout <- Stdout()

      stringContainer <- Container("hello")
      stdout.println($stringContainer)

      integerContainer <- Container(42)
      stdout.println($integerContainer)

Common mistakes

E06060 — Marking a generic type's parameterised constructor as 'private' triggers E06060 - parameterised constructors on a generic type must be public so any parameterisation can instantiate. See ek9 -h E06060 for details.

Incorrect:

      private Container() as pure
        -> initialItem as T

Correct:

      Container() as pure
        -> initialItem as T
Other ways to ask this
  • I'm getting E06060 'generic type does not support private or protected constructors' — what's wrong?
  • Why can't I make my generic class constructor private in EK9?
  • How do I write a generic class constructor that compiles without E06060?
  • The compiler says my parameterised type constructor must be public — why?

Coming from another language?

Java/C# generics allow private generic constructors freely (used with static factory methods). EK9 generic types require public constructors — if you want factory-style controlled construction, use a public constructor PLUS a public factory method that callers reach for by convention. The private-constructor-for-factory idiom does not cross into EK9 generic types.

Keywords: parameterised type, E06060, protected constructor, factory method, private constructor, generic type, public constructor