Kotlin Tutoial

Basics

Control Flow

Array & String

Functions

Collections

OOPs Concept

Exception Handling

Null Safety

Regex & Ranges

Java Interoperability

Miscellaneous

Android

Kotlin Operator Overloading

Kotlin provides the ability to overload operators, allowing custom types (classes) to support operations using standard operators. This is achieved through specific predefined function names.

In this tutorial, we'll cover the basics of operator overloading in Kotlin.

1. Basics of Operator Overloading

To overload an operator, you have to define a member function (or an extension function) with a specific name, and mark it with the operator modifier.

2. Commonly Overloaded Operators

Here are some commonly overloaded operators and their corresponding function names:

  • Unary Operators:

    • +a => unaryPlus()
    • -a => unaryMinus()
    • !a => not()
  • Binary Operators:

    • a + b => plus(b)
    • a - b => minus(b)
    • a * b => times(b)
    • a / b => div(b)
    • a % b => rem(b)
  • Increment and Decrement:

    • a++ or ++a => inc()
    • a-- or --a => dec()
  • Equality and Inequality:

    • a == b => equals(b)
    • a != b => equals(b) (You use the same equals function, the inequality is derived from it.)
  • Comparison:

    • a > b => compareTo(b)
    • a < b => compareTo(b)
    • a >= b => compareTo(b)
    • a <= b => compareTo(b)

3. Examples

Let's demonstrate with a custom Point class:

data class Point(val x: Int, val y: Int) {
    // Overloading + operator
    operator fun plus(other: Point) = Point(x + other.x, y + other.y)
    
    // Overloading unary - operator
    operator fun unaryMinus() = Point(-x, -y)
    
    // Overloading * operator with a scalar
    operator fun times(scalar: Int) = Point(x * scalar, y * scalar)
}

fun main() {
    val p1 = Point(3, 4)
    val p2 = Point(1, 2)

    println(p1 + p2)  // Point(4, 6)
    println(-p1)      // Point(-3, -4)
    println(p1 * 3)   // Point(9, 12)
}

4. In-place Operations

Kotlin also supports in-place operations on mutable types, such as +=, -=, *=, etc. These operations are represented by special functions:

  • a += b => plusAssign(b)
  • a -= b => minusAssign(b)
  • a *= b => timesAssign(b)

5. Other Operators

There are other operators you can overload, such as:

  • Access Operators (get and set):

    • a[i] => get(i)
    • a[i] = value => set(i, value)
  • In:

    • a in b => b.contains(a)
  • Invoke:

    • If you define an invoke operator, you can call instances of the class as if they were functions.

Caution

While operator overloading can make code concise and expressive, you should use it with caution. Overusing or misusing this feature can make code harder to read and understand. Stick with conventional meanings of operators, and ensure that their behavior is intuitive.

Summary

Operator overloading in Kotlin is a powerful feature that lets you extend or customize the behavior of standard operators for user-defined types. By providing specific function implementations for a set of predefined function names and marking them with the operator modifier, you can achieve elegant and intuitive operations on custom types.

  1. Unary operator overloading in Kotlin:

    • Overload unary operators by defining functions with the unaryMinus, unaryPlus, inc, and dec names.
    data class Point(val x: Int, val y: Int) {
        operator fun unaryMinus() = Point(-x, -y)
    }
    
  2. Binary operator overloading in Kotlin:

    • Binary operators like +, -, *, /, % can be overloaded.
    data class Vector(val x: Int, val y: Int) {
        operator fun plus(other: Vector) = Vector(x + other.x, y + other.y)
    }
    
  3. Overloading comparison operators in Kotlin:

    • Implement compareTo and provide equals for comparison operators (<, >, <=, >=, ==, !=).
    data class Person(val age: Int) : Comparable<Person> {
        override fun compareTo(other: Person): Int = age.compareTo(other.age)
    }
    
  4. Overloading arithmetic operators in Kotlin:

    • Overload arithmetic operators for custom types.
    data class Complex(val real: Double, val imag: Double) {
        operator fun plus(other: Complex) = Complex(real + other.real, imag + other.imag)
    }
    
  5. Custom operators in Kotlin:

    • Define custom operators by overloading the corresponding functions.
    data class Matrix(val values: List<List<Int>>) {
        operator fun times(other: Matrix) = /* Custom multiplication logic */
    }
    
  6. Equals and hashCode methods in operator overloading in Kotlin:

    • When overloading equality operators, override equals and hashCode.
    data class Book(val title: String, val author: String) {
        override fun equals(other: Any?) = /* Custom equality logic */
        override fun hashCode() = /* Custom hashCode logic */
    }
    
  7. Infix notation and operator overloading in Kotlin:

    • Use infix modifier to create infix functions for operators.
    data class Quantity(val value: Int) {
        infix operator fun times(other: Quantity) = Quantity(value * other.value)
    }
    
  8. Overloading index operators in Kotlin:

    • Use get and set functions to overload index operators ([]).
    class CustomList {
        operator fun get(index: Int): String = /* Custom get logic */
        operator fun set(index: Int, value: String) { /* Custom set logic */ }
    }
    
  9. Overloading range operators in Kotlin:

    • Implement iterator to enable range expressions (.., until, downTo).
    class DateRange(val start: LocalDate, val endInclusive: LocalDate) {
        operator fun iterator(): Iterator<LocalDate> = /* Custom iterator logic */
    }
    
  10. Overloading logical operators in Kotlin:

    • Overload logical operators like and, or, not.
    data class Permission(val read: Boolean, val write: Boolean) {
        operator fun and(other: Permission) = Permission(read && other.read, write && other.write)
    }