How do EK9 enumerations differ from Java enums?

← Classes and OOP · Ref: Q100

EK9 enumerations are deliberately simpler than Java enums. They are pure value types with no methods, no fields, and no associated data. Every enum automatically gets 24 capabilities: 14 comparison operators (same-type and string), $, $$, #?, ?, #<, #>, #^, three constructors, and built-in iteration.

KEY DIFFERENCES FROM JAVA

1. No methods or fields — behaviour goes in standalone functions, data in Dicts (composition over enum methods).
2. Safe string construction — Priority("Invalid") returns unset, not IllegalArgumentException.
3. Direct string comparison — 'priority == "High"' works without conversion.
4. Exhaustive switch — adding a value forces updating ALL switches (E07310). Java's default silently swallows new values.

EK9 COMPOSITION PATTERN

  Java: Priority.HIGH.getLabel() with field on enum
  EK9: priorityLabel(Priority.High) with standalone function

See Q99 for creating enumerations. See Q73 for exhaustive enum switch. See Q219 for auto-generated operators. See Q225 for the composition pattern in detail.

Example

defines module qa.oop.enumvsjava

  defines type

    Priority
      Low
      Medium
      High
      Critical

  defines function

    <?-
      In Java this would be a method ON the enum.
      In EK9 behaviour lives in standalone functions.
    -?>
    priorityLabel() as pure
      -> priority as Priority
      <- label as String: switch priority
        <- rtn as String: String()
        case Priority.Low
          rtn: "Low - handle when convenient"
        case Priority.Medium
          rtn: "Medium - handle soon"
        case Priority.High
          rtn: "High - handle today"
        case Priority.Critical
          rtn: "Critical - handle immediately"
        default
          rtn: "No priority set"

    isUrgent() as pure
      -> priority as Priority
      <- rtn as Boolean: priority >= Priority.High

  defines program

    EnumVsJavaDemo()
      stdout <- Stdout()

      // === ALL 24 AUTO-GENERATED CAPABILITIES ===

      high <- Priority.High

      //Same-type comparison (== <> < > <= >= <=>)
      stdout.println(`Ordering: High > Low = ${high > Priority.Low}`)
      stdout.println(`Compare: High <=> Medium = ${high <=> Priority.Medium}`)

      //String comparison (== <> < > <= >= <=>)
      stdout.println(`Equals string: ${high == "High"}`)
      stdout.println(`Not equal string: ${high <> "Low"}`)

      //String conversion ($ #^)
      stdout.println(`Dollar: ${high}`)

      //JSON serialization ($$)
      stdout.println(`JSON: ${$high}`)

      //Hash code (#?)
      stdout.println(`Hash: ${#? high}`)

      //Is set (?)
      stdout.println(`Is set: ${high?}`)

      //First / Last (#< #>)
      stdout.println(`First: ${#< high}`)
      stdout.println(`Last: ${#> high}`)

      // === SAFE STRING CONSTRUCTION: no exception ===
      // Java: Priority.valueOf("Urgent") throws IllegalArgumentException
      // EK9: Priority("Urgent") returns unset

      //Constructor from String (unset if no match)
      fromString <- Priority("High")
      stdout.println(`Valid: ${fromString}, isSet: ${fromString?}`)

      invalid <- Priority("Urgent")
      stdout.println(`Invalid isSet: ${invalid?}`)

      //Copy constructor
      copy <- Priority(high)
      stdout.println(`Copy: ${copy}`)

      //Default constructor (unset)
      unsetPriority <- Priority()
      stdout.println(`Unset isSet: ${unsetPriority?}`)

      // === GUARD: safe string-to-enum parsing ===

      if parsed <- Priority("Critical")
        stdout.println(`Parsed: ${parsed}`)

      // === TRI-STATE: unset enum (no null in EK9) ===

      stdout.println(priorityLabel(unsetPriority))

      // === BEHAVIOUR VIA FUNCTIONS (replaces Java enum methods) ===

      for priority in Priority
        stdout.println(priorityLabel(priority))

      // === DICT FOR ASSOCIATED DATA (replaces Java enum fields) ===

      icons <- {
        Priority.Low: "...",
        Priority.Medium: "(!)",
        Priority.High: "(!!)",
        Priority.Critical: "!!!"
        }

      for priority in Priority
        icon <- icons.getOrDefault(priority, "?")
        stdout.println(`${priority}: ${icon}`)

      // === STREAM PIPELINE: built-in iteration ===
      // Java: Arrays.stream(Priority.values())
      // EK9: cat Priority | ...

      urgent <- cat Priority
        | filter by isUrgent
        | collect as List of Priority
      stdout.println(`Urgent count: ${length urgent}`)

Common mistakes

E50060 — EK9 enumerations cannot have methods. Java developers often expect to call methods like label() on enum values. The compiler reports the method as not resolved. In EK9, behaviour belongs in standalone functions that take the enum as a parameter. See ek9 -h E50060 for details.

Incorrect:

priority.label()

Correct:

priorityLabel(priority)

E07620 — Enumerations only support specific operators. Using negate (-) on an enum instance triggers E07620 — operator not defined. Enumerations support comparison, string conversion, isSet, first/last, and hash operators only. See ek9 -h E07620 for details.

Incorrect:

stdout.println(`Negate: ${- high}`)

Correct:

stdout.println(`First: ${#< high}`)

E07310 — Every switch over an enumeration must cover ALL values. Removing a case triggers E07310 — cases should cover all enumerated values. EK9 does not allow silent omission even when a default is present. See ek9 -h E07310 for details.

Incorrect:

        //case Priority.Critical removed

Correct:

        case Priority.Critical
          rtn: "Critical - handle immediately"

E07620 — Enumerations only support a specific set of operators: comparison (== <> < > <= >= <=>), string conversion ($ #^), JSON ($$), hash (#?), isSet (?), and first/last (#< #>). Using any other operator like bitwise negate (~) on an enum instance produces this error. See ek9 -h E07620 for details.

Incorrect:

stdout.println(`Negate: ${~high}`)

Correct:

stdout.println(`Is set: ${high?}`)

E01050 — EK9 normalizes enumeration names (uppercase + remove underscores) to detect confusing duplicates. LOW and low both normalize to LOW. Java allows case-different enum constants but EK9 prevents this source of confusion. See ek9 -h E01050 for details.

Incorrect:

LOW
      low
      High
      Critical

Correct:

Low
      Medium
      High
      Critical
Other ways to ask this
  • Why are EK9 enums simpler than Java enums?
  • Can EK9 enums have methods and fields like Java?
  • What can Java enums do that EK9 enumerations cannot?
  • How do I migrate Java enum patterns to EK9?

Coming from another language?

Java: enums are full classes with fields/methods, valueOf() throws. Python: Enum with methods, KeyError on invalid. Kotlin: enum class with properties. EK9: pure value types, 24 auto-generated capabilities, no methods/fields, safe string construction returns unset.

Keywords: composition, java, string, enumeration, construction, switch, unset, type, comparison, exhaustive, value, Dict, operator, simple, function, valueOf, migrate, field, method, tri-state