Skip to main content
The equality operator (==) evaluates the equivalence of two objects. By default, it determines if two variables reference the same object in memory (identity equality), but it can be overridden to establish structural equality based on object state.

Semantics and Dispatch

In Dart, the expression x == y is syntactic sugar for a method call with built-in null safety handling. The runtime executes the following logic:
  1. Identity Check: If x and y are the same instance (reference the same memory location) or both are null, return true.
  2. Null Check: If only one operand is null, return false.
  3. Method Invocation: Return the result of x.operator==(y).
Because the runtime handles the null check before invoking the method, the implementation of operator== inside a class does not need to check if the argument is null.

Default Implementation

The base implementation in the Object class relies solely on identity. It uses the identical() function to check if the pointers are equal.
// Conceptual representation of Object.operator==
bool operator ==(Object other) {
  return identical(this, other);
}

Overriding for Structural Equality

To compare objects based on their internal values (fields) rather than their memory address, a class must override the == operator. A valid override must adhere to the mathematical properties of an equivalence relation:
  • Reflexive: a == a is always true.
  • Symmetric: a == b implies b == a.
  • Transitive: a == b and b == c implies a == c.

Syntax

class Vector {
  final int x;
  final int y;

  const Vector(this.x, this.y);

  @override
  bool operator ==(Object other) {
    // 1. Check identity (optimization)
    if (identical(this, other)) return true;

    // 2. Check type compatibility
    // If 'other' is not a Vector, return false.
    // Dart performs type promotion, treating 'other' as Vector after this check.
    if (other is! Vector) return false;

    // 3. Check structural equality (field comparison)
    return x == other.x && y == other.y;
  }
  
  // See "HashCode Contract" below
  @override
  int get hashCode => Object.hash(x, y);
}

The HashCode Contract

Dart enforces a contract between operator == and get hashCode. If two objects are considered equal according to operator ==, they must have the same hashCode. Failure to override hashCode when overriding == will result in undefined behavior when the objects are used in hash-based collections (e.g., Set, Map).

Type Safety

The parameter type for operator == is Object, not the specific class type. This allows comparison between objects of different types without throwing runtime errors. In Dart 2.12 and later (Null Safety), the signature is bool operator ==(Object other). The parameter is non-nullable because the runtime guarantees that null is never passed to this method (handled in the dispatch step). Dart allows the use of covariant to tighten the parameter type, though this is generally discouraged for operator == as it violates the standard contract where comparison against an unrelated type should return false rather than throw a TypeError.
// Non-standard approach using covariant
bool operator ==(covariant Vector other) { ... } 
// Risk: vector == "string" throws a runtime error instead of returning false.
Master Dart with Deep Grasping Methodology!Learn More