What you'll learn
This module rounds out the basics: the operators you calculate and compare with, Kotlin's neat ranges, and the smart casts that save you from redundant type conversions.
By the end you'll be able to:
- Use arithmetic, comparison, and logical operators
- Tell apart structural (
==) and referential (===) equality - Work with ranges and the
inoperator - Check types with
isand benefit from smart casts
Operators
Kotlin's operators look familiar. One thing to note, coming from Python: Int division truncates — 17 / 5 is 3, not 3.4. Convert a value to Double first if you want a decimal result:
val a = 17
val b = 5
println(a + b) // 22
println(a / b) // 3 — Int / Int truncates
println(a % b) // 2 — remainder
println(a.toDouble() / b) // 3.4 — convert for a decimal resultNote
a + b calls a.plus(b). That's what lets you give your own types natural syntax later (Module 24, operator overloading).== vs ===
Kotlin has two kinds of equality, and the distinction matters:
- Structural equality
==— do they have the same contents? (This calls.equals().) This is what you almost always want. - Referential equality
===— are they the exact same object in memory?
val a = "hello"
val b = "hello"
println(a == b) // true — structural (same contents)
println("Kotlin".length == 6)// true
println(3 in 1..5) // true — 'in' checks membershipTip
== on objects checks reference identity and trips up beginners — Kotlin's == does the sensible thing and compares contents. Use == for values; reach for === only in the rare case you truly need object identity.Ranges
Ranges are a lovely Kotlin feature. 1..10 creates a range you can loop over or test membership against with in. Variations let you exclude the end, count down, or step:
1..10
1 to 10, inclusive
1 until 10
1 to 9 (end excluded)
10 downTo 1
counts downward
1..10 step 2
1, 3, 5, 7, 9
println(3 in 1..10) // true
println(15 in 1..10) // false
for (i in 1..5) print(i) // 12345
println()
for (i in 0..10 step 2) print("$i ") // 0 2 4 6 8 10is & smart casts
The is operator checks an object's type. Kotlin then does something clever: after an is check, it smart casts the value automatically, so you can use it as that type without an explicit cast. No more (String) x ceremony:
fun describe(x: Any): String {
if (x is String) {
return "A string of length ${x.length}" // smart cast to String
}
if (x is Int) {
return "The number ${x + 1}" // smart cast to Int
}
return "Something else"
}
println(describe("Kotlin"))
println(describe(42))
println(describe(3.14))Note
as?, which returns null instead of throwing: val s = x as? String. Combined with the null-safety tools from Module 3, you handle uncertain types gracefully.Recap & quick check
Key takeaways
- Int / Int truncates in Kotlin — convert to Double for a decimal result.
- Operators are function calls under the hood (a + b calls a.plus(b)).
- == is structural (contents, via equals); === is referential (same object). Prefer ==.
- Ranges: 1..10 (inclusive), until (exclusive end), downTo, step; test with 'in'.
- 'is' checks a type and smart-casts the value, so no explicit cast is needed; use as? for safe casts.
Quick check
1. What is 17 / 5 in Kotlin?
2. Which operator compares contents (structural equality)?
3. What does 3 in 1..10 evaluate to?
4. What does a 'smart cast' let you skip after an 'is' check?
5. What does 'as?' do?
Great — you've got Kotlin's operators and type checks down. Next up: Module 5 — Control Flow.