skip to content

Operators

How Kotlin's operators are defined and what they mean: arithmetic and comparison, the two kinds of equality, range and membership, and the fact that every operator maps to a specific function name. Understanding that mapping is what makes operator overloading and custom collections make sense.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

explore

questions

20

What does 7 / 2 evaluate to in Kotlin, and how do you get a fractional result instead?

level: juniorimportance: must knowfreq 70%

answer

  1. Int / Int = Int, truncates toward zero
  2. Promote one operand: 7.0 / 2
  3. val d: Double = 7 / 2 is still 3.0
  4. No implicit widening — use .toDouble()
  5. BigDecimal for money

basics

~10 s

7 / 2 is 3, because dividing two whole numbers throws away the part after the decimal point. To get 3.5, make at least one number a decimal, like 7.0 / 2.

solid answer

~40 s

Division of two integers performs integer (truncating toward zero) division, so 7 / 2 == 3 and -7 / 2 == -3. The result type is the integer type of the operands. To get a floating-point result you must promote at least one operand to a Double or Float: 7.0 / 2, 7 / 2.0, or 7.toDouble() / 2 all yield 3.5. Kotlin never auto-widens Int to Double for you, so writing val x: Double = 7 / 2 still computes 3 in Int arithmetic first and then assigns 3.0 — a classic bug. The % operator gives the remainder (7 % 2 == 1). For exact decimal math (money) use BigDecimal instead of Double.

code

kotlin · 8 lines
kotlin
fun main() {
    println(7 / 2)            // 3
    println(7.0 / 2)          // 3.5
    println(7 / 2.0)          // 3.5
    val bug: Double = 7 / 2   // 3.0  <-- computed as Int first
    println(bug)
    println(7 % 2)            // 1
}

go deeper

for a junior

Knows 7 / 2 == 3 and that you promote an operand to a Double for 3.5.

for a middle

Explains the result-type rule and the val x: Double = 7 / 2 trap clearly.

for a senior

Discusses truncation toward zero vs floorDiv, explicit conversion philosophy, and Double-for-money pitfalls.

for a principal

Frames Kotlin's no-implicit-widening as a deliberate safety/design choice and weighs BigDecimal/Long-cents strategies for domain correctness.

## Integer vs floating-point division In Kotlin the `/` operator is overloaded per numeric type. When **both** operands are integer types (`Int`, `Long`, `Short`, `Byte`), `/` performs **integer division**: it divides and discards any fractional part, truncating **toward zero**. - `7 / 2` -> `3` (not 3.5) - `-7 / 2` -> `-3` (truncates toward zero, not toward negative infinity) - `1 / 2` -> `0` The **result type** follows the operands: `Int / Int` produces `Int`, `Long / Long` produces `Long`. ## Getting a fractional result Kotlin does **not** implicitly widen `Int` to `Double`. You must make at least one operand floating point so the `Double`/`Float` overload of `/` is chosen: ```kotlin 7 / 2 // 3 (Int division) 7.0 / 2 // 3.5 (Double division — literal 7.0 is Double) 7 / 2.0 // 3.5 7.toDouble() / 2 // 3.5 (explicit conversion) val bug: Double = 7 / 2 // 3.0 — division happens in Int FIRST, then 3 widens to 3.0 ``` That last line is the classic trap: the assignment target type does **not** change how the expression is evaluated; `7 / 2` is computed as `Int` arithmetic (`3`) and only then converted to `3.0`. ## Conversion functions Kotlin requires **explicit** conversions: `.toDouble()`, `.toFloat()`, `.toLong()`, `.toInt()`, etc. There is no automatic numeric promotion as in Java for assignment. ## Remainder The `%` operator returns the remainder: `7 % 2 == 1`, `-7 % 2 == -1` (sign follows the dividend). ## Money / exactness Because `Double` is binary floating point, `0.1 + 0.2 != 0.3`. For currency or exact decimals use `java.math.BigDecimal`.

  • Why does val x: Double = 7 / 2 give 3.0 and not 3.5?
    The right side is evaluated independently using Int division (7 / 2 = 3); the Double target type only triggers a widening of the already-computed 3 to 3.0.
  • How does Int division round — toward zero or toward negative infinity?
    Toward zero. So -7 / 2 is -3, not -4. (Math.floorDiv gives floored division if you need -4.)

Integer division is like sharing cookies among kids: 7 cookies, 2 kids, each gets 3 and you ignore the leftover crumb (that crumb is what % gives you).

saying these in an interview costs you the question

  • Believing 7 / 2 returns 3.5
  • Thinking the assignment type changes how the expression is computed
  • Assuming Kotlin auto-widens Int to Double like Java assignment
  • Saying Int division rounds, when it truncates
  • Using Double for money instead of BigDecimal

context

open as a page

In Kotlin, what is the difference between == and ===? Which one calls equals()?

level: juniorimportance: must knowfreq 85%

basics

~10 s

== checks if two values are equal (same contents) by calling equals(). === checks if two variables point to the exact same object in memory. Use == for value checks.

open as a page

In Kotlin, what does the `operator` keyword do, and how does an expression like `a + b` connect to a function?

level: juniorimportance: must knowfreq 65%

basics

~10 s

Operators like + or [] are shortcuts. Kotlin turns them into calls to specially named functions you mark with the operator keyword. So a + b actually calls a.plus(b).

open as a page

What does the .. operator do in Kotlin, and how do you check whether a value falls inside the resulting range?

level: juniorimportance: must knowfreq 70%

basics

~10 s

The .. operator builds a range, like 1..10 meaning 1 through 10 inclusive. You check if something is inside it with the in keyword, for example x in 1..10.

open as a page

In Kotlin, what do the comparison operators <, >, <=, >= actually translate to under the hood?

level: middleimportance: must knowfreq 65%

basics

~10 s

They all call a single method named compareTo. The compiler rewrites a < b into a.compareTo(b) < 0, and similarly for the others, comparing the returned number against zero.

open as a page

If you override equals() for == to behave correctly in a HashSet/HashMap, what else must you do and why?

level: middleimportance: must knowfreq 55%

basics

~10 s

You must also override hashCode() so equal objects have the same hash. Hash-based collections use hashCode() to find the bucket, then equals() to confirm. Override one without the other and lookups break.

open as a page

How does the Kotlin compiler translate a == b, and why does that make == null-safe?

level: middleimportance: must knowfreq 70%

basics

~20 s

The compiler turns a == b into a null-check plus equals(). If a is null it doesn't call equals(); it just checks whether b is also null. So == never crashes on a null value.

open as a page

What is the difference between .. and ..< in Kotlin, and which operator functions do they map to?

level: middleimportance: must knowfreq 60%

basics

~10 s

1..10 includes both ends (1 through 10). 1..<10 excludes the top end (1 through 9). The first uses rangeTo, the second uses rangeUntil.

open as a page

How do you perform bitwise operations in Kotlin given there are no &, |, ^, <<, >> symbols?

level: middleimportance: should knowfreq 45%

basics

~20 s

Kotlin uses named functions you write between the values, like a and b, a or b, a xor b, a shl 2, a shr 2. There are no &, |, ^, <<, or >> symbols like in Java.

open as a page

How do the indexing operators `a[i]` and `a[i] = v` map to functions, and how does Kotlin support multi-argument indexing like `matrix[r, c]`?

level: middleimportance: should knowfreq 50%

basics

~10 s

Reading a[i] calls a.get(i). Writing a[i] = v calls a.set(i, v). You can pass several indices: a[r, c] calls get(r, c), and the value to store is always the last argument of set.

open as a page

Explain the `invoke` operator (`a()`) and the `contains` operator (`x in a`). What are common, idiomatic uses of each?

level: middleimportance: should knowfreq 45%

basics

~10 s

If a type has an operator fun invoke, you can call its instance like a function: obj(). The in operator checks membership: x in a calls a.contains(x) and returns true or false.

open as a page

How does the compiler translate x in range and x !in range, and why does that let you use in on non-range types?

level: middleimportance: should knowfreq 50%

basics

~10 s

x in y becomes y.contains(x), and x !in y becomes !y.contains(x). Any type that defines a contains function works with in, not just ranges — lists, sets, and strings do too.

open as a page

What happens on Int overflow in Kotlin arithmetic, and why doesn't `val total: Long = bigInt * bigInt` save you?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Int math silently wraps around when it gets too big — no error. Assigning to a Long doesn't help, because the multiply already happened as Int and overflowed before the result became a Long. Make an operand a Long first.

open as a page

How do == and equals() differ when comparing Double values, specifically for NaN and -0.0?

level: seniorimportance: should knowfreq 45%

basics

~10 s

For Doubles used directly, == follows IEEE 754: NaN == NaN is false and -0.0 == 0.0 is true. But equals() (and boxed/generic comparisons) flips this: NaN.equals(NaN) is true and (-0.0).equals(0.0) is false.

open as a page

How does `compareTo` back the comparison operators, and what conventions/contracts must operator functions like `compareTo`, `equals`, and `plusAssign` honor?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Operators <, >, <=, >= all call one function: compareTo, which returns an Int. Negative means less, zero means equal, positive means greater. Operator functions must obey their expected contracts so comparisons and equality behave consistently.

open as a page

How does Kotlin destructuring (`val (a, b) = pair`) work under the hood, and what are the rules for `componentN` functions?

level: seniorimportance: should knowfreq 48%

basics

~10 s

Destructuring splits an object into parts. val (a, b) = p calls p.component1() and p.component2(). Data classes generate these automatically; for other classes you write operator fun componentN yourself.

open as a page

How does in drive for-loop iteration over ranges, and how do you iterate descending or with a step?

level: seniorimportance: should knowfreq 45%

basics

~10 s

for (i in 1..5) loops 1 to 5 because the range can be iterated. To go down use downTo (5 downTo 1), and to skip values use step (1..10 step 2).

open as a page

How would you make Kotlin's +, *, and < work on a custom Vector2 class, and what rules govern those operator functions?

level: seniorimportance: nice to knowfreq 30%

basics

~10 s

Write functions with special names marked with the operator keyword: plus for +, times for *, and compareTo for <. Kotlin then lets callers use the symbols directly on your type.

open as a page

When is === (referential equality) the right tool, and what are the risks of relying on it — for example with boxed Int caching?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

Use === when you truly care about object identity: checking something is the same instance, like a cache hit or breaking a reference cycle. Avoid it for value comparison because identical-looking values may or may not be the same object.

open as a page

How can you build a range over a custom Comparable type, and what does ClosedRange give you that an iterable progression does not?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

If your type implements Comparable, you get .. for free via the rangeTo extension. That builds a closed range you can test with in, but you cannot loop over it unless you also define how to step through values.

open as a page