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
- Create a complex number class that implements basic arithmetic operator overloading
- Implement a fraction class that supports four basic operations and comparison
- Write a program demonstrating all types of operator precedence
- Design a matrix class that implements basic matrix operations
- Create a currency class that implements safe currency operations (considering exchange rates and precision)