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 SUPER class's matching operator first, 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 - one missing operator at a time (e.g. <=>, ==, $, #?, ?).
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.
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 no operators, there is nothing for the derived default to chain to, so E07190 is raised once per missing operator (<=>, ==, $, #?, ?). 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:
Shape as open name as String: String() // no operators defined Circle extends Shape radius as Float: 0.0 default operator
Correct:
Shape as open name as String: String() Shape() -> name as String this.name :=: name
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: operator, base, default, override, inherit, E07190, super, extends