Implement #? manually on a class that combines multiple fields.

← Operators and Expressions · Ref: Q1053

Combine the hash values of each field using arithmetic. The #? prefix operator gets the hash of individual fields.

  operator #? as pure
    <- rtn as Integer: #?firstName + #?lastName + #?age

Each #?field calls _hashcode() on that field. Adding them produces a combined hash. For better distribution, multiply by a prime:

  operator #? as pure
    <- rtn as Integer: (#?firstName * 31) + (#?lastName * 17) + #?age

Alternatively, use 'default operator' to auto-generate #? from all fields. Manual implementation is only needed when you want a custom hash strategy (for example, hashing on a subset of fields).

The #? operator must be pure, take no parameters, and return Integer.

See Q911 for what #? means. See Q1047 for #? on records with default operator. See Q1055 for default vs manual operators.

Example

defines module qa.operators.hashcodeclassmulti

  defines class

    Employee
      firstName as String: String()
      lastName as String: String()
      employeeId as Integer: 0

      default private Employee()

      Employee()
        ->
          firstName as String
          lastName as String
          employeeId as Integer
        this.firstName :=: firstName
        this.lastName :=: lastName
        this.employeeId :=: employeeId

      //Custom #? using subset of fields
      operator #? as pure
        <- rtn as Integer: (#?firstName * 31) + (#?lastName * 17) + #?employeeId

      operator == as pure
        -> other as Employee
        <- rtn as Boolean: employeeId == other.employeeId

      override operator ? as pure
        <- rtn as Boolean: firstName? and lastName? and employeeId?

      operator $ as pure
        <- rtn as String: `${firstName} ${lastName} (${employeeId})`

  defines program

    HashcodeClassDemo()
      stdout <- Stdout()

      emp1 <- Employee("Alice", "Smith", 1001)
      emp2 <- Employee("Alice", "Smith", 1001)
      emp3 <- Employee("Bob", "Jones", 1002)

      stdout.println(`emp1 hash: ${#? emp1}`)
      stdout.println(`emp2 hash: ${#? emp2}`)
      stdout.println(`emp3 hash: ${#? emp3}`)
      stdout.println(`emp1 == emp2: ${emp1 == emp2}`)

Common mistakes

E07550 — The #? operator must return Integer. Using Float or any other type triggers E07550.

Incorrect:

      operator #? as pure
        <- rtn as Float: #?firstName + #?lastName

Correct:

      operator #? as pure
        <- rtn as Integer: (#?firstName * 31) + (#?lastName * 17) + #?employeeId
Other ways to ask this
  • Write a custom hashcode operator for a class with several fields
  • How do I build a hashcode from multiple fields in EK9?
  • Show me a multi-field #? implementation on a class

Coming from another language?

Java: Objects.hash(field1, field2). Python: hash((f1, f2)). EK9: #?field1 + #?field2 or 'default operator'.

Keywords: hashcode, operator, class, manual, multi-field, custom