Why can't I reassign an injected component variable?

← DI Validation · Ref: Q669

Injected dependencies are set by the DI container and cannot be directly reassigned (E08170). The container manages the lifecycle, and manual reassignment would break the contract between application wiring and injection sites.

WHY NO REASSIGNMENT

The application definition declares which concrete component satisfies each abstract injection point. If code reassigns the variable, the wiring becomes unpredictable.

GUARDED ASSIGNMENT ALLOWED

Use ':=?' for conditional fallback:

  service as MyService!
  service :=? fallbackService    // Only assign if not injected

This respects the container's decision: if injection succeeded, the guard does nothing.

See Q667 for abstract injection. See Q668 for injectable contexts. See Q670 for field initialization.

Example

defines module qa.divalidation.reassignment

  defines component

    <?-
      Abstract cache contract.
    -?>
    CacheService as abstract

      lookup() as abstract
        -> cacheKey as String
        <- cachedValue as String?

      default operator ?

    <?-
      Simple in-memory cache implementation.
    -?>
    InMemoryCache extends CacheService

      override lookup()
        -> cacheKey as String
        <- cachedValue as String: ""

        cachedValue: "cached:" + cacheKey

      default operator ?

  defines application

    CacheApp
      register InMemoryCache() as CacheService

  defines program

    InjectionReassignmentDemo() with application of CacheApp
      stdout <- Stdout()

      cache as CacheService!
      directLookup <- cache.lookup("settings")
      stdout.println(`Lookup: ${directLookup}`)

      anotherLookup <- cache.lookup("user-profile")
      stdout.println(`Another: ${anotherLookup}`)

Common mistakes

E08170 — Injected component variables are set by the DI container and cannot be reassigned with ':='. The application wiring defines which implementation satisfies each injection point, and manual reassignment would break that contract. See ek9 -h E08170 for details.

Incorrect:

      cache as CacheService!
      cache := InMemoryCache()
      directLookup <- cache.lookup("settings")

Correct:

      cache as CacheService!
      directLookup <- cache.lookup("settings")
Other ways to ask this
  • What is E08170 reassignment of injected variable?
  • Can I change an injected dependency at runtime?
  • How do I handle optional injection?

Coming from another language?

Java: Spring injection is final by convention (not enforced). Python: no enforcement. Go: manual wiring can be changed. Rust: ownership prevents aliasing. EK9: compiler prevents reassignment of injected variables, guarded assignment allowed.

Keywords: registration, isset, validate, component, guarded, null-safe, injection, DI, E08170, immutable, inject, safe, reassignment