Can dynamic classes use dependency injection in EK9?
← Dependency Injection · Ref: Q233
Dynamic classes (anonymous trait implementations) can have injection fields. The compiler follows the call graph from programs through functions to find all dynamic class instantiations and validates their injection fields.
DYNAMIC CLASS WITH INJECTION
A dynamic class created with '() with trait of' can declare '!' fields:
handler <- () with trait of Processable logger as Logger! override process() <- rtn as String: logger.log("processing")
The Logger injection is resolved from the application context.
CALL GRAPH ANALYSIS
The compiler traces the execution path from the program through any functions that create dynamic classes. If a function creates a dynamic class with injection fields, those fields must be satisfiable from the program's application.
WHY THIS IS UNIQUE
Most DI frameworks only support injection on named, registered classes. EK9 extends injection to anonymous inline implementations, which is powerful for callbacks, handlers, and one-off implementations that still need access to registered services.
COMPILE-TIME SAFETY
As with all EK9 DI, dynamic class injection is validated at compile time. If the required component is not registered, the compiler reports the error.
See Q227 for compile-time validation. See Q111 for component basics. See Q115 for dynamic class basics. See Q234 for component lifecycle.
Example
defines module qa.di.dynamic defines trait Processor process() as abstract -> input as String <- output as String? override operator ? as pure <- rtn as Boolean: true defines component Logger as abstract log() as abstract -> message as String <- result as String? default operator ? ConsoleLogger is Logger override log() -> message as String <- result as String: "LOG: " + message default operator ? defines function createProcessor() <- handler as Processor? // === DYNAMIC CLASS WITH INJECTION === handler: () with trait of Processor logger as Logger! override process() -> input as String <- output <- String() output: logger.log(input) default operator ? defines application DynamicApp register ConsoleLogger() as Logger defines program DynamicInjectionDemo() with application of DynamicApp stdout <- Stdout() // === FUNCTION CREATES DYNAMIC CLASS WITH INJECTION === handler <- createProcessor() result <- handler.process("test message") stdout.println(result) stdout.println("Dynamic class injection validated at compile time")
Common mistakes
E08150 — Even in dynamic classes, injection fields must reference abstract component types. The concrete type is determined by the application registration, not the injection field declaration. See ek9 -h E08150 for details.
Incorrect:
logger as ConsoleLogger!
Correct:
logger as Logger!
E07160 — Removing the only registration from the application leaves it empty, requiring at least one implementation to be provided. See ek9 -h E07160 for details.
Correct:
register ConsoleLogger() as Logger
Other ways to ask this
- Do anonymous trait implementations support injection in EK9?
- How does DI work with inline classes in EK9?
- Can I inject components into dynamic classes?
Coming from another language?
Java: anonymous inner classes cannot use @Autowired, must receive dependencies via constructor or enclosing scope. Guice: no injection into anonymous classes. .NET: no injection into anonymous types. Python: no built-in DI for anonymous objects. Go: no anonymous classes. Rust: no anonymous classes with DI. EK9: dynamic classes with '() with trait of' support '!' injection fields, compiler traces call graph to validate.
Keywords: trait, capture, anonymous, call, dynamic, injection, inject, class, graph, inline, handler, closure, callback