Skip to main content
The less-than operator (<) is a binary operator that compares a receiver (left-hand operand) against an argument (right-hand operand). While conventionally used to return a boolean indicating strict inequality, the operator is method-based and can be defined to return any type.

Syntax

receiver < argument

Semantics

The expression a < b is syntactic sugar for a method invocation. The behavior depends on the static type of the receiver a.

Static Resolution

If a is not dynamic, the compiler resolves the operator in the following order:
  1. Instance Member: The compiler looks for an instance method named operator < defined on the static type of a or inherited from a superclass.
  2. Extension Member: If no instance member is found, the compiler looks for an applicable extension method that defines operator < for the type of a.
Note: Extension methods cannot shadow instance members. If a type already defines operator < (e.g., int), an extension method defining < on that same type will be ignored in favor of the instance member.

Dynamic Resolution

If the static type of a is dynamic, the lookup occurs at runtime. The runtime system attempts to invoke the operator < method on the actual runtime type of the object. If the method does not exist or the argument type is incompatible, a NoSuchMethodError or TypeError is thrown.

Declaration

To define the < operator for a class or extension, use the operator keyword followed by the < symbol.
ReturnType operator <(ParameterType parameter) {
  // Implementation
}

Type Constraints

  • Receiver: The type of the left operand must define or inherit the operator < method, or have an applicable extension method.
  • Argument: The right operand must be assignable to the parameter type defined in the method signature.
  • Return Type: The Dart language specification permits any return type. However, to use the expression in boolean contexts (such as if statements, assert, or logical && operations), the operator must return bool.

Defining Operator Logic

Custom types define comparison logic by implementing the operator as an instance method.
class Rank {
  final int value;
  const Rank(this.value);

  // Defines the logic for: rankInstance < other
  bool operator <(Rank other) {
    return this.value < other.value;
  }
}

void main() {
  final r1 = Rank(1);
  final r2 = Rank(5);
  
  // Resolves to the instance method defined in Rank
  print(r1 < r2); // true
}

Extension Methods

The < operator can be defined for types that do not natively implement it using extension methods. This applies only when the type does not already possess an instance method for <.
class Wrapper {
  final int id;
  Wrapper(this.id);
}

// Defining < on Wrapper via extension because Wrapper has no instance operator <
extension WrapperComparator on Wrapper {
  bool operator <(Wrapper other) => this.id < other.id;
}

void main() {
  final w1 = Wrapper(10);
  final w2 = Wrapper(20);

  // Resolves to the extension method in WrapperComparator
  print(w1 < w2); // true
}

Null Safety

The < operator is not defined by default for nullable types (T?). The class Null does not define the < operator, and the Object class does not define it.
int? x = null;
// Compile-time error: The operator '<' isn't defined for the type 'int?'.
// print(x < 10);
To perform a comparison on a nullable receiver, the value must be unwrapped (e.g., x! < 10) or checked explicitly (e.g., x != null && x < 10).
Master Dart with Deep Grasping Methodology!Learn More