When you write `1 + 2L` or `3 + 0.5`, no `.toLong()`/`.toDouble()` call appears. How does Kotlin produce the wider result type without implicit widening of variables?
answer
- Operators overloaded per type pair
- Return type = wider operand
- Byte+Byte = Int, not Byte
- 1/2 == 0 (integer division)
- Convert before, not after, to avoid overflow
basics
~20 sMath operators like + come in many versions, one per type combination. Int.plus(Long) returns a Long. So the operator itself handles the mix and gives the wider result — it's not the variable being auto-converted.
solid answer
~40 sKotlin numeric types each declare **overloaded operator functions** for every other numeric type. For example `Int` has `plus(Long): Long`, `plus(Double): Double`, etc. So `1 + 2L` resolves to `Int.plus(Long)` which returns `Long`; `3 + 0.5` resolves to `Int.plus(Double)` returning `Double`. The result takes the **wider** of the two operand types. This is operator overloading, not implicit widening of a variable: there is still no rule that lets you assign a bare `Int` to a `Long`. The operators internally convert the narrower operand and compute in the wider representation. Beware: pure integer arithmetic stays in Int unless an operand is Long — `Int.MAX_VALUE + 1` overflows silently to a negative Int, and `1 / 2` is integer division (`0`). To force the wider type, make one literal/operand wide: `1L / 2` or `1.0 / 2`.
code
kotlin · 7 linesval a = 1 + 2L // Long (Int.plus(Long))
val b = 3 + 0.5 // Double
val c = 1 / 2 // 0 (Int.div(Int))
val d = 1.0 / 2 // 0.5
val e: Byte = 1; val f: Byte = 2
val g = e + f // Int, not Byte
val h = Int.MAX_VALUE.toLong() + 1 // convert BEFORE to avoid overflowgo deeper
Knows mixed arithmetic yields the wider type and that 1/2 is 0.
Identifies overloaded operator functions as the mechanism and that result type is the wider operand.
Explains Byte/Short promote to Int and why converting before vs after matters for overflow.
Articulates how operator overloading preserves the 'no implicit widening' invariant while staying ergonomic, and the overflow-correctness implications for numeric code.
## The mechanism: overloaded operators Kotlin doesn't widen variables implicitly, but each numeric type defines a family of **operator functions** (`plus`, `minus`, `times`, `div`, `rem`) overloaded for every numeric argument type. `Int` declares roughly: ```kotlin operator fun plus(other: Byte): Int operator fun plus(other: Short): Int operator fun plus(other: Int): Int operator fun plus(other: Long): Long operator fun plus(other: Float): Float operator fun plus(other: Double): Double ``` So overload resolution picks the version matching the right-hand operand, and the **return type is the wider of the two**. `1 + 2L` -> `Int.plus(Long)` -> `Long`. `3 + 0.5` -> `Int.plus(Double)` -> `Double`. ## Why this isn't implicit widening The key distinction: you still cannot write `val l: Long = anInt`. The 'automatic' result type comes from **function return types chosen by overload resolution**, not from a subtype/coercion rule. The operator converts the narrower operand inside its implementation. ## Traps that follow from the rules - **Integer division**: `1 / 2 == 0` because both operands are `Int` and `Int.div(Int): Int` truncates. Use `1.0 / 2` or `1 / 2.0` for `0.5`. - **Silent overflow**: `Int.MAX_VALUE + 1` wraps to `Int.MIN_VALUE`; the result type is `Int`, so widening afterward (`.toLong()`) doesn't recover the lost value. Convert *before* the operation: `Int.MAX_VALUE.toLong() + 1`. - **Mixed promotes to the wider type** but not beyond: `byteA + byteB` returns an **Int**, not a Byte — Byte/Short arithmetic promotes to Int (there are no `Byte.plus(Byte): Byte` operators). ## Practical rule Make one operand the target width *before* arithmetic to control both the result type and overflow behavior. Don't rely on `.toLong()` after the fact.
- What type does `byteA + byteB` produce, and why?An `Int`. Byte and Short have no same-type arithmetic operators returning Byte/Short; their operands are promoted and the result is `Int`.
- Why does converting after an Int overflow not help, e.g. `(Int.MAX_VALUE + 1).toLong()`?The `+` already happened in Int and overflowed to a negative value; `.toLong()` faithfully widens that wrong result. You must convert an operand to Long before adding.
saying these in an interview costs you the question
- Calling mixed-operand arithmetic 'implicit widening of variables'
- Expecting `byteA + byteB` to be a Byte
- Forgetting `1 / 2 == 0`
- Believing post-overflow `.toLong()` recovers the value