When should I use 'by' delegation instead of manual method forwarding?
← Code Quality · Ref: Q958
When a class implements a trait without 'by', has a compatible delegate field, and manually overrides 70% or more of the trait methods (minimum 5 methods), the compiler raises E11023.
WHY THIS MATTERS
Manually forwarding every method to a delegate field is boilerplate that the 'by' keyword eliminates. The compiler detects this pattern and enforces cleaner code.
THRESHOLD
All three conditions must be met:
1. The trait has at least 5 abstract methods
2. 70% or more are overridden in this class
3. A compatible field of the trait type exists
THIS EXAMPLE
The TaskHandler class implements Orchestrator (7 abstract methods) and overrides all 7. However, it has NO compatible delegate field of type Orchestrator, so E11023 does not trigger.
The typicalError mutation adds a delegate field, causing the checker to detect manual forwarding that should use 'by'.
See Q770 for trait by delegation basics. See Q313 for code smells catalog.
Example
defines module qa.quality.missingbydelegation defines trait Orchestrator checkPreconditions() abstract executeCore() abstract handleCompletion() abstract inspectState() abstract prepareOutput() abstract runCleanup() abstract finalizeResult() abstract defines class OrchestratorImpl with trait of Orchestrator override checkPreconditions() require true override executeCore() require true override handleCompletion() require true override inspectState() require true override prepareOutput() require true override runCleanup() require true override finalizeResult() require true defines class <?- TaskHandler implements Orchestrator with all 7 methods overridden. No compatible delegate field exists, so E11023 does NOT trigger. Adding a delegate field of type Orchestrator would trigger E11023. -?> TaskHandler with trait of Orchestrator override checkPreconditions() require true override executeCore() require true override handleCompletion() require true override inspectState() require true override prepareOutput() require true override runCleanup() require true override finalizeResult() require true default operator ? defines program MissingByDelegationDemo() stdout <- Stdout() stdout.println("TaskHandler overrides all 7 methods without delegate field") stdout.println("No E11023 because no compatible delegate field exists")
Common mistakes
E11023 — Adding a compatible delegate field of type Orchestrator while overriding all 7 methods triggers E11023. The compiler sees 7/7 (100%) overrides with a compatible field and requires 'by delegate'. See ek9 -h E11023 for details.
Incorrect:
TaskHandler with trait of Orchestrator delegate Orchestrator: OrchestratorImpl() override checkPreconditions() delegate.checkPreconditions()
Correct:
TaskHandler with trait of Orchestrator override checkPreconditions() require true
Other ways to ask this
- What triggers E11023 MISSING_BY_DELEGATION?
- Why does the compiler complain about manual delegation?
- How does EK9 detect redundant delegation boilerplate?
Coming from another language?
Java: no delegation syntax, manual forwarding always allowed. Kotlin: 'by' exists but optional. Go: struct embedding is all-or-nothing. EK9: compiler detects when 'by' should be used and enforces it.
Keywords: override, threshold, forwarding, quality, trait, by, clean-code, boilerplate, E11023, delegation