What does 'default operator' generate?
← Classes and OOP · Ref: Q116
In EK9, the 'default operator' keyword generates standard operators automatically based on a type's fields. It provides equality, comparison, string conversion, hashing, and isSet checking.
WHAT IT GENERATES
The following operators are auto-generated:
== equality (field-by-field comparison) <> inequality (negation of ==) <=> comparison (returns Integer, field-by-field ordering) $ string conversion (concatenation of field strings) #? hashcode (combined hash of all fields) ? isSet (true when all fields are set)
HOW IT WORKS
For each field in declaration order, the generated operator compares, concatenates, or hashes. Comparison uses the first field as primary sort, second as secondary, etc.
WHEN TO USE IT
Use 'default operator' when field-by-field behaviour is correct. This covers most value types, records, and simple classes.
WHEN NOT TO USE IT
Override specific operators when you need custom logic: computed equality, selective comparison, custom string formatting, or partial isSet checking.
RECORD VS CLASS DEFAULTS
Both records and classes support 'default operator'. The generated behaviour is identical, but records expose fields publicly while classes keep them private.
See Q96 for custom operators. See Q93 for class basics. See Q97 for records. See Q98 for record operators. See Q99 for enumeration auto-operators. See Q238 for the complete fixed operator set. See Q242 for conversion operators. See Q245 for implementing a complete custom type.
Example
defines module qa.oop.defaultoperator defines class Colour red <- Integer() green <- Integer() blue <- Integer() Colour() -> red as Integer green as Integer blue as Integer this.red: red this.green: green this.blue: blue default operator //Demonstrates individual default operator Weight grams <- Float() Weight() -> grams as Float this.grams: grams default operator == default operator <> default operator <=> default operator $ default operator #? default operator ? defines trait Displayable display() as pure abstract <- rtn as String? override operator ? as pure <- rtn as Boolean: true defines record Address street <- String() city <- String() Address() -> street as String city as String this.street: street this.city: city default operator defines program DefaultOperatorDemo() stdout <- Stdout() // === EQUALITY (==) and INEQUALITY (<>) === red1 <- Colour(255, 0, 0) red2 <- Colour(255, 0, 0) blue <- Colour(0, 0, 255) stdout.println(`Equal: ${red1 == red2}`) stdout.println(`Not equal: ${red1 <> blue}`) // === COMPARISON (<=>) === stdout.println(`Compare: ${red1 <=> blue}`) // === STRING ($) === stdout.println(`String: ${red1}`) // === ISSET (?) === stdout.println(`IsSet: ${red1?}`) // === RECORD with default operator === addr1 <- Address("Main St", "Springfield") addr2 <- Address("Main St", "Springfield") stdout.println(`Address equal: ${addr1 == addr2}`) stdout.println(`Address: ${addr1}`)
Common mistakes
E08180 — Class fields must be initialised either with a default value or through constructor assignment. Using 'Integer()' provides a default unset value. Declaring 'red as Integer' without '?' or a value may trigger E08180 if no constructor sets it. See ek9 -h E08180 for details.
Incorrect:
red as Integer
Correct:
red <- Integer()
E50060 — EK9 does not have an 'equals()' method. Use the == operator directly for equality comparison. See ek9 -h E50060 for details.
Incorrect:
stdout.println(red1.equals(red2))
Correct:
stdout.println(`Equal: ${red1 == red2}`)
E50060 — EK9 does not have a compareTo() method. Use the <=> operator directly for comparison. The 'default operator' auto-generates <=> for you. See ek9 -h E50060 for details.
Incorrect:
stdout.println(red1.compareTo(blue))
Correct:
stdout.println(`Compare: ${red1 <=> blue}`)
E50060 — EK9 does not have a toString() method. Use the $ operator or string interpolation. The 'default operator' auto-generates $ for string conversion. See ek9 -h E50060 for details.
Incorrect:
stdout.println(addr1.toString())
Correct:
stdout.println(`Address: ${addr1}`)
E07220 — Not all operators support 'default' auto-generation. Operators like 'contains' have no meaningful field-by-field generation semantics. Only ==, <>, <=>, $, #?, and ? can be auto-generated. See ek9 -h E07220 for details.
Incorrect:
default operator contains
Correct:
default operator ==
E07030 — Traits cannot have 'default operator' because traits define contracts, not data-based implementations. Auto-generation needs concrete fields which traits do not have. Provide explicit operator implementations in traits instead. See ek9 -h E07030 for details.
Incorrect:
default operator
Correct:
override operator ? as pure <- rtn as Boolean: true
E07235 — Any aggregate with fields must define operator ? for EK9's tri-state semantics. Without it, guard expressions and safe access patterns cannot inspect field state. Use 'default operator ?' or implement manually. See ek9 -h E07235 for details.
Incorrect:
//operator ? omitted
Correct:
default operator ?
Other ways to ask this
- Which operators does 'default operator' create automatically?
- How does auto-generated operator work in EK9?
- What is the difference between 'default operator' and custom operators?
Coming from another language?
Java: must manually write equals(), hashCode(), toString(), compareTo(). Lombok @Data generates them. Records (Java 16) auto-generate equals/hashCode/toString. Python: @dataclass generates __eq__, __repr__, __hash__. Rust: #[derive(Eq, Hash, Ord, Debug)] generates trait implementations. Go: no auto-generation, must write comparison functions manually. Kotlin: data class generates equals(), hashCode(), toString(), copy(). EK9: 'default operator' generates ==, <>, <=>, $, #?, ? from all fields in declaration order.
Keywords: generate, operator, default, hashcode, string, automatic, field, comparison, object-oriented, equality, isset