Why does 'default operator' in a derived class fail when the base class has no operators?
← Override Mechanics · Ref: Q1314
A 'default operator' in a derived class generates operators that call the NEAREST matching operator above it first - the super's, or one further up when the classes in between add no fields and declare none - then combine with the derived fields. If the base class has no operators (no 'default operator' and no manual operators), there is nothing for the derived default to chain to, so EK9 raises E07190 naming the first operator it cannot chain to (<=>).
Fix - choose one of two approaches:
APPROACH 1 (recommended): add 'default operator' to the base class so the super has operators to call. Then 'default operator' in the derived class chains cleanly.
defines class Shape as open name as String: String() default operator // base now has operators Circle extends Shape radius as Float: 0.0 default operator // chains to super's operators
APPROACH 2: implement the operators manually in the derived class instead of using 'default operator', writing each operator (?, $, <=>, etc.) explicitly.
Note that an aggregate with fields also needs 'override operator ?' or 'default operator ?' to define set/unset semantics, so adding 'default operator' to the base fixes both concerns at once. Likewise a derived class that ADDS fields declares its own '$', '$$' and ':=:' rather than inheriting the base's, which cannot know the new fields (E07238) - the bare 'default operator' in Circle does that.
See Q1069 for mixing default operator with a custom override. See Q577 for override operator basics.
Example
defines module qa.overridemechanics.defaultsuperoperator defines class //THE FIX: the base class defines 'default operator' so the super has //operators (<=>, ==, $, #?, ?) for the derived default to chain to. Shape as open name as String: String() Shape() -> name as String this.name :=: name default operator //The derived 'default operator' generates operators that call super's //matching operator first, then combine with the derived fields. Circle extends Shape radius as Float: 0.0 Circle() -> name as String radius as Float super(name) this.radius :=: radius default operator defines program DefaultSuperOperatorDemo() stdout <- Stdout() c1 <- Circle("small", 1.0) c2 <- Circle("large", 5.0) stdout.println(`Circle set: ${c1?}`) stdout.println(`Circle: ${c1}`) stdout.println(`Equal: ${c1 == Circle("small", 1.0)}`) stdout.println(`Compare: ${c1 <=> c2}`)
Common mistakes
E07190 — A derived 'default operator' generates operators that call the super's matching operator first. When the base class (Shape) defines only 'operator ?' and nothing else, there is nothing for the derived default to chain to, so the compiler reports E07190 for the first missing operator it needs (<=>). Add 'default operator' to the base class so the super has operators to call, or implement the operators manually in the derived class instead of defaulting. See ek9 -h E07190 for details.
Incorrect:
this.name :=: name default operator ?
Correct:
this.name :=: name default operator
E07238 — Circle adds a field (radius) but declares only '?', so it would INHERIT Shape's '$', '$$' and ':=:' - operators written for Shape's fields, which cannot know about radius: the string and JSON would silently leave it out and a copy would copy only half the object. A type that adds fields declares its own '$', '$$' and ':=:' (as it must declare '?', E07235); the bare 'default operator' declares them all, each calling Shape's first. See ek9 -h E07238 for details.
Incorrect:
this.radius :=: radius default operator ?
Correct:
this.radius :=: radius default operator
Other ways to ask this
- What triggers E07190 default operator missing in super?
- Why can't I use default operator when my parent class defines no operators?
- How do I fix 'default of operators requires super to have appropriate operator'?
Coming from another language?
Java: equals/hashCode/toString are inherited from Object, so a subclass's generated/IDE versions always have a super to call - no compile-time check ties them together. Kotlin data classes do not support inheritance of generated equals/hashCode at all (data classes are effectively final). EK9: 'default operator' explicitly chains to the super's operator and the compiler enforces (E07190) that the super actually defines the operator being defaulted, rather than silently producing inconsistent behaviour.
Keywords: E07238, inherit, default, extends, super, E07190, operator, override, base