ProgrammingBackend Developer / Middle Kotlin Developer

What is operator overloading in Kotlin, how to use it correctly, and what pitfalls arise when overloading operators? Provide a detailed example and explain best practices.

Pass interviews with Hintsage AI assistant

Answer

In Kotlin, operator overloading is supported through the operator keyword. This allows you to define your own logic for standard operators (+, -, *, [], ==, etc.) for user-defined types:

  • To do this, you need to declare a method with the required name and mark it with the operator modifier.
  • The list of possible overloadable operators is fixed by the language.

Example:

data class Vec2(val x: Int, val y: Int) { operator fun plus(other: Vec2) = Vec2(x + other.x, y + other.y) } val a = Vec2(1,2) val b = Vec2(2,3) val c = a + b // will invoke operator fun plus

Nuances and best practices:

  • Maintain expected behavior (for example, == and equals should be consistent).
  • Do not abuse operator syntax if it is not obvious for the type.
  • It is recommended to overload only those operators that are truly logical for your type.

Trick Question

What is the difference between overloading the == operator and overriding the equals() method?

Answer: The == operator in Kotlin always calls the equals() method (with a preliminary null check). If you define operator fun equals(other: Any?): Boolean, the == operator will always use this method. You cannot make == and equals() work differently.

data class Point(val x: Int, val y: Int) { override operator fun equals(other: Any?): Boolean { ... } } val a = Point(1,2) val b = Point(1,2) println(a == b) // will invoke a.equals(b)

History

Overloaded the compareTo operator without explicit semantics:

In the Point class, operator fun compareTo was defined for the ability to use it in sorting. However, "greater than" and "less than" had no explicit meaning, different team members wrote different logic. The result was confusing bugs during collection sorting.


History

Forgot about symmetry of operators for mutual types:

A developer implemented operator fun plus(a: Matrix, b: Vector): Matrix, but did not ensure functionality for Vector + Matrix, as they did not implement a symmetric function. As a result, the expression vector + matrix did not compile, only matrix + vector worked. This led to bugs in many parts of the code.


History

Incorrect operator get overloading:

A developer created a custom class, overloading operator fun get(index: Int): Foo. They did not add boundary checks — and attempting to access a non-existent element returned an object with invalid fields instead of throwing an exception — which led to "invisible" errors in business logic.