#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
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
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
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
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
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
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
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
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
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
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
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
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
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
// 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
// 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
// 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)