Skip to content

Operators

Overview

Kotlin provides a rich set of operators, including arithmetic, comparison, logical, and bitwise operators. Kotlin's operator overloading mechanism allows you to define operator behavior for custom types, making code more intuitive and readable.

Arithmetic Operators

Basic Arithmetic Operations

kotlin
fun main() {
    val a = 10
    val b = 3
    
    println("Arithmetic operator examples:")
    println("a = $a, b = $b")
    println("a + b = ${a + b}")  // Addition
    println("a - b = ${a - b}")  // Subtraction
    println("a * b = ${a * b}")  // Multiplication
    println("a / b = ${a / b}")  // Division (integer division)
    println("a % b = ${a % b}")  // Modulo
    
    // Floating-point arithmetic
    val x = 10.0
    val y = 3.0
    println("\nFloating-point arithmetic:")
    println("x = $x, y = $y")
    println("x / y = ${x / y}")  // Floating-point division
    
    // Unary operators
    println("\nUnary operators:")
    println("+a = ${+a}")   // Unary plus
    println("-a = ${-a}")   // Unary minus
    println("++a = ${++a}") // Pre-increment (note: a's value will change)
    println("a-- = ${a--}") // Post-decrement
    println("a = $a")       // Check a's final value
}

Compound Assignment Operators

kotlin
fun main() {
    var number = 10
    
    println("Initial value: $number")
    
    number += 5   // Equivalent to number = number + 5
    println("number += 5: $number")
    
    number -= 3   // Equivalent to number = number - 3
    println("number -= 3: $number")
    
    number *= 2   // Equivalent to number = number * 2
    println("number *= 2: $number")
    
    number /= 4   // Equivalent to number = number / 4
    println("number /= 4: $number")
    
    number %= 3   // Equivalent to number = number % 3
    println("number %= 3: $number")
}

Comparison Operators

Equality and Comparison

kotlin
fun main() {
    val a = 10
    val b = 20
    val c = 10
    
    println("Comparison operator examples:")
    println("a = $a, b = $b, c = $c")
    
    // Equality comparison
    println("a == b: ${a == b}")   // Structural equality
    println("a == c: ${a == c}")
    println("a != b: ${a != b}")   // Not equal
    
    // Size comparison
    println("a < b: ${a < b}")     // Less than
    println("a > b: ${a > b}")     // Greater than
    println("a <= c: ${a <= c}")   // Less than or equal
    println("a >= c: ${a >= c}")   // Greater than or equal
    
    // Referential equality
    val str1 = "Hello"
    val str2 = "Hello"
    val str3 = String("Hello".toCharArray())
    
    println("\nReferential equality:")
    println("str1 == str2: ${str1 == str2}")   // Structural equality: true
    println("str1 === str2: ${str1 === str2}") // Referential equality: true (string pool)
    println("str1 == str3: ${str1 == str3}")   // Structural equality: true
    println("str1 === str3: ${str1 === str3}") // Referential equality: false
}

Nullable Type Comparison

kotlin
fun main() {
    val a: Int? = 10
    val b: Int? = null
    val c: Int? = 10
    
    println("Nullable type comparison:")
    println("a = $a, b = $b, c = $c")
    
    // Safe comparison
    println("a == b: ${a == b}")   // false
    println("a == c: ${a == c}")   // true
    println("b == null: ${b == null}")  // true
    
    // Using safe call for comparison
    println("a?.compareTo(c ?: 0): ${a?.compareTo(c ?: 0)}")
}

Logical Operators

Boolean Logic

kotlin
fun main() {
    val isTrue = true
    val isFalse = false
    
    println("Logical operator examples:")
    println("isTrue = $isTrue, isFalse = $isFalse")
    
    // Logical AND
    println("isTrue && isFalse: ${isTrue && isFalse}")
    println("isTrue && isTrue: ${isTrue && isTrue}")
    
    // Logical OR
    println("isTrue || isFalse: ${isTrue || isFalse}")
    println("isFalse || isFalse: ${isFalse || isFalse}")
    
    // Logical NOT
    println("!isTrue: ${!isTrue}")
    println("!isFalse: ${!isFalse}")
    
    // Short-circuit evaluation
    println("\nShort-circuit evaluation examples:")
    fun expensiveOperation(): Boolean {
        println("Executing expensive operation")
        return true
    }
    
    println("false && expensiveOperation(): ${false && expensiveOperation()}")  // Won't execute
    println("true || expensiveOperation(): ${true || expensiveOperation()}")    // Won't execute
}

Bitwise Operators

Bit Operations

kotlin
fun main() {
    val a = 0b1010  // 10 in decimal
    val b = 0b1100  // 12 in decimal
    
    println("Bitwise operator examples:")
    println("a = $a (${a.toString(2)})")
    println("b = $b (${b.toString(2)})")
    
    // Bitwise operations
    println("a shl 1 = ${a shl 1} (${(a shl 1).toString(2)})")  // Left shift
    println("a shr 1 = ${a shr 1} (${(a shr 1).toString(2)})")  // Arithmetic right shift
    println("a ushr 1 = ${a ushr 1} (${(a ushr 1).toString(2)})") // Logical right shift
    
    println("a and b = ${a and b} (${(a and b).toString(2)})")   // Bitwise AND
    println("a or b = ${a or b} (${(a or b).toString(2)})")      // Bitwise OR
    println("a xor b = ${a xor b} (${(a xor b).toString(2)})")   // Bitwise XOR
    println("a.inv() = ${a.inv()} (${a.inv().toString(2)})")     // Bitwise inversion
    
    // Practical application: Permission checking
    println("\nPermission checking example:")
    val READ = 1    // 0001
    val WRITE = 2   // 0010
    val EXECUTE = 4 // 0100
    
    var permissions = READ or WRITE  // 0011
    println("Initial permissions: ${permissions.toString(2)}")
    
    // Check permissions
    println("Has read permission: ${(permissions and READ) != 0}")
    println("Has write permission: ${(permissions and WRITE) != 0}")
    println("Has execute permission: ${(permissions and EXECUTE) != 0}")
    
    // Add permission
    permissions = permissions or EXECUTE
    println("After adding execute: ${permissions.toString(2)}")
    println("Has execute permission: ${(permissions and EXECUTE) != 0}")
}

Range Operators

Range Creation and Operations

kotlin
fun main() {
    // Closed range
    val range1 = 1..10        // 1 to 10 (inclusive)
    val range2 = 'a'..'z'     // Character range
    
    // Open range
    val range3 = 1 until 10   // 1 to 9 (exclusive of 10)
    
    // Descending range
    val range4 = 10 downTo 1  // 10 to 1
    
    println("Range operator examples:")
    println("1..10: $range1")
    println("'a'..'z': $range2")
    println("1 until 10: $range3")
    println("10 downTo 1: $range4")
    
    // Range check
    val number = 5
    println("\nRange checking:")
    println("$number in 1..10: ${number in range1}")
    println("$number !in 1..10: ${number !in range1}")
    
    // Character range check
    val char = 'm'
    println("'$char' in 'a'..'z': ${char in range2}")
    
    // Range iteration
    println("\nRange iteration:")
    print("1..5: ")
    for (i in 1..5) {
        print("$i ")
    }
    println()
    
    print("10 downTo 6 step 2: ")
    for (i in 10 downTo 6 step 2) {
        print("$i ")
    }
    println()
}

Type Check and Cast Operators

is and as Operators

kotlin
fun main() {
    val items: List<Any> = listOf("Hello", 42, 3.14, true, null)
    
    println("Type checking and casting:")
    for (item in items) {
        when {
            item is String -> {
                // Smart cast: item is automatically cast to String
                println("String: '$item', length: ${item.length}")
            }
            item is Int -> {
                println("Integer: $item, squared: ${item * item}")
            }
            item is Double -> {
                println("Double: $item, rounded: ${item.toInt()}")
            }
            item is Boolean -> {
                println("Boolean: $item")
            }
            item == null -> {
                println("Null value")
            }
            else -> {
                println("Unknown type: $item")
            }
        }
    }
    
    // Type casting
    println("\nType casting examples:")
    val obj: Any = "Hello, Kotlin!"
    
    // Unsafe cast
    try {
        val str = obj as String
        println("Cast successful: $str")
    } catch (e: ClassCastException) {
        println("Cast failed: ${e.message}")
    }
    
    // Safe cast
    val str: String? = obj as? String
    val int: Int? = obj as? Int
    
    println("Safe cast to String: $str")
    println("Safe cast to Int: $int")
}

Elvis Operator

Null Value Handling

kotlin
fun main() {
    val nullableString: String? = null
    val anotherNullable: String? = "Hello"
    
    // Elvis operator ?:
    val result1 = nullableString ?: "Default value"
    val result2 = anotherNullable ?: "Default value"
    
    println("Elvis operator examples:")
    println("nullableString ?: 'Default value' = '$result1'")
    println("anotherNullable ?: 'Default value' = '$result2'")
    
    // Chained Elvis operator
    val first: String? = null
    val second: String? = null
    val third: String? = "Third value"
    
    val chainResult = first ?: second ?: third ?: "Final default"
    println("Chained Elvis: '$chainResult'")
    
    // Combined with function calls
    fun getName(): String? = null
    fun getDefaultName(): String = "Default name"
    
    val name = getName() ?: getDefaultName()
    println("Combined with function: '$name'")
    
    // Early return
    fun processUser(user: User?) {
        val validUser = user ?: return  // Return early if user is null
        println("Processing user: ${validUser.name}")
    }
    
    processUser(null)
    processUser(User("Alice"))
}

data class User(val name: String)

Operator Overloading

Custom Operators

kotlin
data class Point(val x: Int, val y: Int) {
    // Overload addition operator
    operator fun plus(other: Point): Point {
        return Point(x + other.x, y + other.y)
    }
    
    // Overload subtraction operator
    operator fun minus(other: Point): Point {
        return Point(x - other.x, y - other.y)
    }
    
    // Overload multiplication operator (scalar multiplication)
    operator fun times(scalar: Int): Point {
        return Point(x * scalar, y * scalar)
    }
    
    // Overload unary minus operator
    operator fun unaryMinus(): Point {
        return Point(-x, -y)
    }
    
    // Overload comparison operator
    operator fun compareTo(other: Point): Int {
        val thisDistance = x * x + y * y
        val otherDistance = other.x * other.x + other.y * other.y
        return thisDistance.compareTo(otherDistance)
    }
}

// Extension operator overloading
operator fun Int.times(point: Point): Point {
    return Point(this * point.x, this * point.y)
}

fun main() {
    val point1 = Point(3, 4)
    val point2 = Point(1, 2)
    
    println("Operator overloading examples:")
    println("point1 = $point1")
    println("point2 = $point2")
    
    // Using overloaded operators
    println("point1 + point2 = ${point1 + point2}")
    println("point1 - point2 = ${point1 - point2}")
    println("point1 * 2 = ${point1 * 2}")
    println("3 * point1 = ${3 * point1}")
    println("-point1 = ${-point1}")
    
    // Comparison operators
    println("point1 > point2: ${point1 > point2}")
    println("point1 < point2: ${point1 < point2}")
}

Collection Operator Overloading

kotlin
class Matrix(private val data: Array<IntArray>) {
    val rows = data.size
    val cols = if (rows > 0) data[0].size else 0
    
    // Overload index access operator
    operator fun get(row: Int, col: Int): Int {
        return data[row][col]
    }
    
    operator fun set(row: Int, col: Int, value: Int) {
        data[row][col] = value
    }
    
    // Overload contains operator
    operator fun contains(value: Int): Boolean {
        return data.any { row -> row.contains(value) }
    }
    
    // Overload iterator
    operator fun iterator(): Iterator<Int> {
        return data.flatten().iterator()
    }
    
    override fun toString(): String {
        return data.joinToString("\n") { row ->
            row.joinToString(" ") { "%3d".format(it) }
        }
    }
}

fun main() {
    val matrix = Matrix(arrayOf(
        intArrayOf(1, 2, 3),
        intArrayOf(4, 5, 6),
        intArrayOf(7, 8, 9)
    ))
    
    println("Matrix operator overloading:")
    println(matrix)
    
    // Using index access
    println("\nmatrix[1, 1] = ${matrix[1, 1]}")
    
    // Modify element
    matrix[1, 1] = 10
    println("After modification matrix[1, 1] = ${matrix[1, 1]}")
    
    // Using contains
    println("Matrix contains 5: ${5 in matrix}")
    println("Matrix contains 15: ${15 in matrix}")
    
    // Iteration
    print("All elements: ")
    for (element in matrix) {
        print("$element ")
    }
    println()
}

Operator Precedence

Precedence Examples

kotlin
fun main() {
    println("Operator precedence examples:")
    
    // Arithmetic operator precedence
    val result1 = 2 + 3 * 4      // 14, not 20
    val result2 = (2 + 3) * 4    // 20
    println("2 + 3 * 4 = $result1")
    println("(2 + 3) * 4 = $result2")
    
    // Comparison and logical operators
    val a = 5
    val b = 10
    val c = 15
    
    val result3 = a < b && b < c  // true && true = true
    val result4 = a < b || b > c  // true || false = true
    println("$a < $b && $b < $c = $result3")
    println("$a < $b || $b > $c = $result4")
    
    // Elvis operator precedence
    val nullable: String? = null
    val result5 = nullable?.length ?: 0
    println("nullable?.length ?: 0 = $result5")
    
    // Assignment operator has lowest precedence
    var x = 0
    x += 2 * 3  // x = x + (2 * 3) = 6
    println("x += 2 * 3, x = $x")
}

Practical Examples

Mathematical Vector Class

kotlin
data class Vector2D(val x: Double, val y: Double) {
    // Vector addition
    operator fun plus(other: Vector2D) = Vector2D(x + other.x, y + other.y)
    
    // Vector subtraction
    operator fun minus(other: Vector2D) = Vector2D(x - other.x, y - other.y)
    
    // Scalar multiplication
    operator fun times(scalar: Double) = Vector2D(x * scalar, y * scalar)
    
    // Dot product
    infix fun dot(other: Vector2D) = x * other.x + y * other.y
    
    // Vector magnitude
    val magnitude: Double
        get() = kotlin.math.sqrt(x * x + y * y)
    
    // Unit vector
    fun normalize(): Vector2D {
        val mag = magnitude
        return if (mag != 0.0) Vector2D(x / mag, y / mag) else Vector2D(0.0, 0.0)
    }
    
    override fun toString() = "Vector2D(%.2f, %.2f)".format(x, y)
}

// Extension operator
operator fun Double.times(vector: Vector2D) = vector * this

fun main() {
    val v1 = Vector2D(3.0, 4.0)
    val v2 = Vector2D(1.0, 2.0)
    
    println("Vector operation examples:")
    println("v1 = $v1")
    println("v2 = $v2")
    println("v1 + v2 = ${v1 + v2}")
    println("v1 - v2 = ${v1 - v2}")
    println("v1 * 2.0 = ${v1 * 2.0}")
    println("2.0 * v1 = ${2.0 * v1}")
    println("v1 dot v2 = ${v1 dot v2}")
    println("v1 magnitude = ${v1.magnitude}")
    println("v1 normalized = ${v1.normalize()}")
}

Best Practices

1. Operator Overloading Principles

kotlin
// Good practice: Operator semantics should be intuitive
data class Money(val amount: Double, val currency: String) {
    operator fun plus(other: Money): Money {
        require(currency == other.currency) { "Currency types don't match" }
        return Money(amount + other.amount, currency)
    }
    
    operator fun times(multiplier: Double): Money {
        return Money(amount * multiplier, currency)
    }
}

// Avoid: Non-intuitive operator overloading
// class User {
//     operator fun plus(other: User): User { ... }  // Adding users has no clear meaning
// }

2. Null-Safe Operator Usage

kotlin
// Good practice: Use null-safe operators appropriately
fun processUser(user: User?) {
    // Use safe call
    val name = user?.name ?: "Unknown"
    
    // Use let to avoid repeated null checks
    user?.let { u ->
        println("Processing user: ${u.name}")
        // More operations...
    }
}

// Avoid: Overusing not-null assertion
fun badExample(user: User?) {
    // Dangerous: May throw KotlinNullPointerException
    // println(user!!.name)
}

3. Operator Precedence

kotlin
// Good practice: Use parentheses to clarify precedence
fun calculateScore(base: Int, multiplier: Int, bonus: Int): Int {
    return (base * multiplier) + bonus  // Clear precedence
}

// Avoid: Relying on implicit precedence can be confusing
fun confusingCalculation(a: Int, b: Int, c: Int): Int {
    return a + b * c  // May not be clear enough
}

Next Steps

After mastering operators, let's learn about Kotlin's conditional statements, including if expressions and when expressions.

Next Chapter: Conditional Statements

Exercises

  1. Create a complex number class that implements basic arithmetic operator overloading
  2. Implement a fraction class that supports four basic operations and comparison
  3. Write a program demonstrating all types of operator precedence
  4. Design a matrix class that implements basic matrix operations
  5. Create a currency class that implements safe currency operations (considering exchange rates and precision)

Content is for learning and research only.