How do I create a singleton in EK9?

← Classes and OOP · Ref: Q117

EK9 has no traditional singleton pattern. There is no private constructor trick, no static getInstance() method, and no object keyword. The ONLY way to have a single shared instance is through Dependency Injection and Inversion of Control using the component and application registration system.

WHY NO TRADITIONAL SINGLETONS

Traditional singletons create hidden global state, tight coupling, and testing nightmares. EK9 enforces that shared instances are explicitly registered and injected, making dependencies visible and testable.

HOW SINGLE INSTANCES WORK

Register a component in an application definition. All injection points receive the same instance:

  defines component
    AppConfig as abstract
      setting() as abstract
        <- rtn as String?
      default operator ?
  defines application
    MyApp
      register ProductionConfig() as AppConfig

The '!' suffix on a type declaration triggers injection: config as AppConfig!

LIFECYCLE

The registered component instance is created once during application startup. Every injection point across the application receives the same instance.

THREAD SAFETY

Since components are shared, use immutable state or MutexLock for mutable data. Pure methods ensure thread-safe reads.

See Q111 for components and DI. See Q114 for aspects. See Q119 for constructs overview. See Q234 for component lifecycle details.

See Q330 for why EK9 uses singleton-only DI scope.

Example

defines module qa.oop.singleton

  defines component

    AppConfig as abstract
      setting() as abstract
        <- rtn as String?
      default operator ?

    ProductionConfig extends AppConfig
      override setting()
        <- rtn as String: "production-mode"
      default operator ?

  defines application

    SingletonApp
      register ProductionConfig() as AppConfig

  defines program

    SingletonDemo() with application of SingletonApp
      stdout <- Stdout()

      // === SINGLETON via component injection ===

      config1 as AppConfig!
      config2 as AppConfig!

      // Both reference the same registered instance
      stdout.println(`Config1: ${config1.setting()}`)
      stdout.println(`Config2: ${config2.setting()}`)

      // === Same value from both injection points ===

      stdout.println(`Same: ${config1.setting() == config2.setting()}`)

Common mistakes

E05120 — When implementing an abstract method from the parent component AppConfig, the 'override' keyword is mandatory. ProductionConfig must use 'override setting()'. See ek9 -h E05120 for details.

Incorrect:

setting()

Correct:

override setting()

E50040 — Without 'as abstract', AppConfig has an abstract method 'setting()' but the component itself is not marked abstract. The compiler requires types with abstract methods to be declared 'as abstract'. See ek9 -h E50040 for details.

Incorrect:

AppConfig

Correct:

AppConfig as abstract

E50060 — EK9 does not use Java-style getter naming. The method is 'setting()' not 'getSetting()'. See ek9 -h E50060 for details.

Incorrect:

config1.getSetting()

Correct:

config1.setting()

E50060 — EK9 does not have a toString() method. Use string interpolation or the $ operator instead. See ek9 -h E50060 for details.

Incorrect:

stdout.println(config1.toString())

Correct:

stdout.println(`Config1: ${config1.setting()}`)
Other ways to ask this
  • Does EK9 support the singleton pattern?
  • How do I ensure only one instance of a class exists?
  • What is the EK9 approach to singletons?

Coming from another language?

Java: private constructor + static getInstance(), or Spring @Singleton/@Scope. Python: module-level instances, or __new__ override. Rust: lazy_static! or once_cell for global state. Go: sync.Once for lazy initialisation, package-level variables. Kotlin: 'object' keyword creates a singleton. EK9: component registration provides singleton lifecycle, injection with '!' suffix, no explicit singleton pattern needed, thread safety through pure methods or MutexLock.

Keywords: thread, lifecycle, inject, register, define, singleton, shared, object-oriented, safety, pattern, instance, component