What are components in EK9?
← Classes and OOP · Ref: Q111
Components in EK9 are constructs designed for dependency injection (DI). They are registered in application definitions and injected into programs and other components using the '!' suffix.
COMPONENT BASICS
Define a component under 'defines component':
defines component Repository as abstract findAll() as abstract <- rtn as List of String? default operator ?
ABSTRACT COMPONENTS
Define abstract components as contracts. Concrete implementations extend them:
InMemoryRepository extends Repository override findAll() <- rtn as List of String: ["item1", "item2"]
INJECTION
Use the '!' suffix to inject a registered component:
repository as Repository!
The runtime resolves which concrete implementation was registered.
APPLICATION REGISTRATION
Register components in an application definition:
defines application MyApp register InMemoryRepository() as Repository
PROGRAM WITH APPLICATION
Link a program to an application for injection:
Program1 with application of MyApp repository as Repository! items <- repository.findAll()
COMPONENT VS CLASS
Components are for DI-managed singletons and services. Classes are for regular objects created directly.
See Q93 for class basics. See Q103 for abstract classes. See Q112 for services. See Q113 for text constructs. See Q114 for aspects. See Q119 for constructs overview. See Q227 for compile-time DI validation. See Q228 for registration ordering. See Q231 for program-application linking.
See Q324 for @Autowired equivalent. See Q325 for @Component equivalent.
See Q667 for abstract injection only. See Q668 for injectable contexts. See Q672 for program-application link.
Example
defines module qa.oop.components defines component Repository as abstract findAll() as abstract <- rtn as List of String? default operator ? InMemoryRepository extends Repository override findAll() <- rtn <- List() of String rtn += "Alice" rtn += "Bob" rtn += "Charlie" default operator ? defines application MyApp register InMemoryRepository() as Repository defines program ComponentDemo() with application of MyApp stdout <- Stdout() // === INJECTION with ! suffix === repository as Repository! // === USE INJECTED COMPONENT === items <- repository.findAll() for item in items stdout.println(`Item: ${item}`) stdout.println(`Total: ${length items}`)
Common mistakes
E05120 — When implementing an abstract method from the parent component, the 'override' keyword is mandatory. InMemoryRepository must use 'override findAll()'. See ek9 -h E05120 for details.
Incorrect:
findAll()
Correct:
override findAll()
E50040 — Without 'as abstract', Repository contains abstract method 'findAll()' but the component itself is not marked abstract. The compiler reports E50040 — cannot be abstract in a non-abstract construct. See ek9 -h E50040 for details.
Incorrect:
Repository
Correct:
Repository as abstract
E07250 — Component methods cannot use the 'protected' access modifier. Components are DI-managed singletons without subclass hierarchies, so 'protected' is meaningless. Use 'private' or 'public' instead. See ek9 -h E07250 for details.
Incorrect:
override protected findAll()
Correct:
override findAll()
E08150 — Only abstract components can be injected. Injecting a concrete component directly bypasses the DI contract — always inject the abstract type and register the concrete implementation in the application. See ek9 -h E08150 for details.
Incorrect:
repository as InMemoryRepository!
Correct:
repository as Repository!
E50010 — Dependency injection with '!' only works with components. DataProcessor is a class, not a component — classes are constructed directly, not injected. Use 'defines component' for injectable types. See ek9 -h E50010 for details.
Incorrect:
processor as DataProcessor!
Correct:
repository as Repository!
E50020 — Changing 'defines component' to 'defines class' makes Repository a class instead of a component. The register statement requires component types, so the compiler rejects it with E50020 — incompatible genus. Components and classes are distinct constructs in EK9. See ek9 -h E50020 for details.
Incorrect:
defines class
Correct:
defines component
E50080 — Repository is declared 'as abstract' and cannot be instantiated directly. The register statement must use a concrete implementation (InMemoryRepository), not the abstract base type. See ek9 -h E50080 for details.
Incorrect:
register Repository() as Repository
Correct:
register InMemoryRepository() as Repository
Other ways to ask this
- How does dependency injection work in EK9?
- What is the component construct in EK9?
- How do I use IoC containers in EK9?
Coming from another language?
Java: Spring @Component/@Service with @Autowired injection, CDI @Inject. Python: no built-in DI, frameworks like dependency-injector or FastAPI Depends. Rust: no built-in DI, manual wiring or shaku crate. Go: no built-in DI, wire or dig packages. Kotlin: Koin or Dagger for DI, no language-level support. EK9: 'defines component' is a language-level construct, 'register X as Y' in application, '!' suffix for injection, compile-time validated DI.
Keywords: injection, dependency, component, register, service, inversion, object-oriented, application, inject, abstract, ioc