Why does E50040 fire on my abstract method in a class but not in a trait?
← Dependency Injection · Ref: Q1291
E50040 fires when an abstract method appears inside a construct that the compiler does not accept as an abstract container. The rule is NOT uniform across all constructs — it matters which kind of construct you are writing.
WHERE 'as abstract' IS REQUIRED ON THE CONTAINING CONSTRUCT
- Classes: a class with an abstract method must be declared 'Shape as abstract'
- Components: a component with an abstract method must be declared 'Logger as abstract'
Why: classes and components are instantiable by default. The compiler assumes you mean a concrete type unless you opt into abstractness. Leaving an abstract method inside a concrete container would produce an incomplete type with no way to construct it safely.
WHERE 'as abstract' IS NOT REQUIRED
- Traits: a trait is an inherently abstract interface-like construct. Abstract methods are the default expectation. You simply declare 'describe() as abstract' inside a trait and it compiles.
- Generic function declarations that happen to be abstract (E50040 is not the error code for those — a different error applies).
COMPARE THE TWO CASES DIRECTLY
defines trait
Printable // no 'as abstract' needed describe() as pure abstract // OK — trait accepts abstract methods <- rtn as String?
defines class
Shape // E50040 on area() below! area() as abstract // because 'Shape' is not 'as abstract' <- rtn as Float?
The trait compiles. The class does not — it reports E50040 on 'area'. Fixing the class means EITHER marking the container 'Shape as abstract' OR dropping 'as abstract' from the method and giving it a default implementation.
DI IMPLICATIONS
For dependency injection hierarchies, the idiomatic EK9 shape is:
- Declare the interface as a TRAIT (no 'as abstract' needed; all methods abstract by default)
- Declare an ABSTRACT CLASS or ABSTRACT COMPONENT that implements the trait and factors out shared state
- Declare CONCRETE subclasses or subcomponents that override the remaining abstract methods
- Register the concrete type in the application
This lets you express 'some shared implementation + some pluggable methods' without fighting E50040. If you find yourself reaching for 'as abstract' on a class or component, ask first whether the abstraction really belongs on a trait — traits are the lighter-weight vehicle for this kind of contract.
See Q1290 for the component-with-abstract-method variant. See Q103 for the full classes/methods/abstract story.
Example
defines module qa.dependencyinjection.e50040classvstrait defines trait Printable describe() as pure abstract <- rtn as String? defines class Shape as abstract area() as pure abstract <- rtn as Float? Circle extends Shape with trait of Printable radius as Float: Float() Circle() -> radius as Float this.radius :=: radius override area() as pure <- rtn as Float: 3.14159 * radius * radius override describe() as pure <- rtn as String: `Circle(r=${radius})` override operator ? as pure <- rtn as Boolean: radius? defines program E50040ClassVsTraitDemo() stdout <- Stdout() c <- Circle(5.0) stdout.println(c.describe()) stdout.println(`area=${c.area()}`)
Common mistakes
E50040 — Declaring 'area() as abstract' inside 'defines class / Shape' without marking Shape itself 'as abstract' fires E50040. Unlike traits, classes are concrete by default and must opt into abstractness. Add 'as abstract' to the class header — 'Shape as abstract' — or convert the declaration to a trait if you only need an interface-like contract.
Incorrect:
Shape
Correct:
Shape as abstract
Other ways to ask this
- When do I need to mark a construct 'as abstract' in EK9 — is it only classes and components?
- Why does my trait compile with an abstract method but my class doesn't?
- What's the difference between trait abstract methods and class abstract methods in EK9?
- How do traits avoid the E50040 rule that classes and components are subject to?
Coming from another language?
Java: every interface method is implicitly abstract; abstract classes require the 'abstract' keyword on the class for abstract methods. EK9: traits behave like Java interfaces (methods implicitly abstract, no container modifier needed); classes and components behave more strictly than Java abstract classes (the container MUST be marked 'as abstract' if it contains any abstract methods). The distinction surfaces in E50040 — it fires on classes and components, never on traits.
Keywords: trait, component, E50040, dependency injection, abstract class, abstract method