Beginner

Operators in Kotlin

Learn arithmetic, comparison, logical, and assignment operators in Kotlin, plus operator overloading and range operators

Operators are the building blocks of expressions in any programming language. Kotlin provides a rich set of operators that go beyond the standard arithmetic and logical operators you’d expect from a JVM language. As a multi-paradigm language with both object-oriented and functional roots, Kotlin treats many operators as function calls behind the scenes — a + b is actually shorthand for a.plus(b).

This design has powerful consequences. Operators can be overloaded for your own types, ranges become first-class citizens with the .. operator, and null-safety operators like ?. and ?: help eliminate entire categories of runtime errors. Kotlin also drops some legacy operators (like the bitwise & and | you’d find in Java) in favor of named infix functions such as and and or.

In this tutorial you’ll learn the full operator landscape: arithmetic, comparison, logical, assignment, range, and the null-safety operators that make Kotlin distinctive.

Arithmetic and Assignment Operators

Kotlin supports the standard arithmetic operators (+, -, *, /, %) and their compound assignment forms (+=, -=, *=, /=, %=). Note that integer division truncates toward zero, just like Java.

Create a file named Operators.kt:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
fun main() {
    // Arithmetic operators
    val a = 17
    val b = 5
    println("a + b = ${a + b}")
    println("a - b = ${a - b}")
    println("a * b = ${a * b}")
    println("a / b = ${a / b}")    // Integer division
    println("a % b = ${a % b}")    // Remainder

    // Floating-point division
    val x = 17.0
    val y = 5.0
    println("x / y = ${x / y}")

    // Compound assignment
    var counter = 10
    counter += 5
    counter -= 2
    counter *= 2
    println("counter = $counter")

    // Increment and decrement
    var n = 1
    n++
    ++n
    println("n = $n")

    // Comparison operators
    println("a == b is ${a == b}")
    println("a != b is ${a != b}")
    println("a > b is ${a > b}")
    println("a <= b is ${a <= b}")

    // Logical operators (short-circuit)
    val isAdult = true
    val hasTicket = false
    println("isAdult && hasTicket = ${isAdult && hasTicket}")
    println("isAdult || hasTicket = ${isAdult || hasTicket}")
    println("!isAdult = ${!isAdult}")

    // String concatenation with +
    val greeting = "Hello, " + "Kotlin!"
    println(greeting)

    // Bitwise operations use infix functions, not symbols
    val flags = 0b1100 and 0b1010
    val mask = 0b1100 or 0b0011
    val shifted = 1 shl 3
    println("and = $flags, or = $mask, shl = $shifted")
}

Operator Precedence

Kotlin’s operator precedence follows mathematical conventions: *, /, % bind tighter than +, -, which bind tighter than comparisons, which bind tighter than && and ||. Use parentheses when in doubt — the compiler won’t complain, and the intent will be clearer to readers.

1
2
val result = 2 + 3 * 4        // 14, not 20
val clearer = 2 + (3 * 4)     // Same result, clearer intent

Ranges, Equality, and Null-Safety Operators

Kotlin introduces several operators that don’t exist in Java. The range operator .. creates a Range object, === checks referential identity (distinct from structural equality ==), and the safe-call ?. and Elvis ?: operators make working with nullable values painless.

Create a file named KotlinOperators.kt:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
fun main() {
    // Range operator
    val oneToFive = 1..5
    println("3 in 1..5 = ${3 in oneToFive}")
    println("7 in 1..5 = ${7 in oneToFive}")

    // until creates a half-open range (excludes upper bound)
    val halfOpen = 1 until 5
    println("5 in 1 until 5 = ${5 in halfOpen}")

    // downTo and step
    print("Countdown:")
    for (i in 5 downTo 1 step 2) {
        print(" $i")
    }
    println()

    // Structural vs referential equality
    val s1 = "Kotlin"
    val s2 = "Kot" + "lin"
    val s3 = s1
    println("s1 == s2 (structural) = ${s1 == s2}")
    println("s1 === s3 (referential) = ${s1 === s3}")

    // Safe-call operator
    val name: String? = null
    val length = name?.length
    println("length via safe call = $length")

    // Elvis operator provides a default
    val safeLength = name?.length ?: 0
    println("length with default = $safeLength")

    // Not-null assertion (use sparingly)
    val nonNull: String? = "present"
    println("forced length = ${nonNull!!.length}")

    // Membership operators
    val fruits = listOf("apple", "banana", "cherry")
    println("'apple' in fruits = ${"apple" in fruits}")
    println("'grape' !in fruits = ${"grape" !in fruits}")

    // Type-check and cast operators
    val anything: Any = "Kotlin"
    if (anything is String) {
        // Smart cast: anything is now treated as String
        println("uppercase = ${anything.uppercase()}")
    }
}

Running with Docker

1
2
3
4
5
6
7
8
# Pull the Kotlin image
docker pull zenika/kotlin:1.4

# Compile and run the basic operators example
docker run --rm -v $(pwd):/app -w /app zenika/kotlin:1.4 sh -c 'kotlinc Operators.kt -include-runtime -d operators.jar && java -jar operators.jar'

# Compile and run the ranges/null-safety example
docker run --rm -v $(pwd):/app -w /app zenika/kotlin:1.4 sh -c 'kotlinc KotlinOperators.kt -include-runtime -d kotlin-operators.jar && java -jar kotlin-operators.jar'

Expected Output

Running Operators.kt:

a + b = 22
a - b = 12
a * b = 85
a / b = 3
a % b = 2
x / y = 3.4
counter = 26
n = 3
a == b is false
a != b is true
a > b is true
a <= b is false
isAdult && hasTicket = false
isAdult || hasTicket = true
!isAdult = false
Hello, Kotlin!
and = 8, or = 15, shl = 8

Running KotlinOperators.kt:

3 in 1..5 = true
7 in 1..5 = false
5 in 1 until 5 = false
Countdown: 5 3 1
s1 == s2 (structural) = true
s1 === s3 (referential) = true
length via safe call = null
length with default = 0
forced length = 7
'apple' in fruits = true
'grape' !in fruits = true
uppercase = KOTLIN

Key Concepts

  • Operators are functionsa + b desugars to a.plus(b), so any class can overload arithmetic operators by implementing the right operator functions.
  • Structural vs referential equality — Use == for value equality (calls equals()) and === for identity (same object reference). This is the opposite of Java’s defaults.
  • No bitwise operator symbols — Kotlin uses infix functions (and, or, xor, shl, shr, inv) instead of &, |, etc. This keeps && and || unambiguous.
  • Ranges are objects1..10, 1 until 10, and 10 downTo 1 produce iterable ranges you can use in for loops, in checks, and collection operations.
  • Null-safety operators eliminate NPEs?. short-circuits on null, ?: provides defaults, and !! asserts non-null (throws if wrong). Prefer the first two; reserve !! for cases where null is genuinely a bug.
  • Smart casts — After an is check, the compiler automatically casts the variable inside the branch, so you don’t need to write the cast explicitly.
  • in works on ranges and collections — The same operator checks membership in a numeric range or a list, making expressions read naturally.

Running Today

All examples can be run using Docker:

docker pull zenika/kotlin:1.4
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining