skip to content

In Kotlin, why does `val l: Long = anInt` fail to compile, and how do you fix it?

level: juniorimportance: must knowfreq 75%

answer

  1. No implicit widening — Int is not a Long
  2. Fix with .toLong()/.toDouble()
  3. Literals adapt to expected type
  4. Operators are overloaded, not widening
  5. Narrowing truncates silently

basics

~10 s

Kotlin never auto-converts a smaller number type to a bigger one. An Int is not a Long. You must convert it yourself by calling a conversion function, for example anInt.toLong().

solid answer

~40 s

Kotlin has no implicit numeric widening: smaller numeric types are not subtypes of wider ones, so assigning an `Int` where a `Long` is expected is a type error, even though every Int value fits in a Long. You fix it with an explicit conversion call: `val l: Long = anInt.toLong()`. The standard library defines `toByte()`, `toShort()`, `toInt()`, `toLong()`, `toFloat()`, `toDouble()`, and `toChar()` on every numeric type. The design avoids the surprising silent conversions Java allows. Note literals are different: `val l: Long = 1` works because the integer literal `1` is typed by its expected context. Also, arithmetic operators like `+` are overloaded across types, so `anInt + 1L` yields a `Long` automatically — that's operator overloading, not implicit widening of a variable.

code

kotlin · 6 lines
kotlin
val anInt: Int = 42
// val l: Long = anInt          // compile error: type mismatch
val l: Long = anInt.toLong()    // explicit conversion
val d: Double = anInt.toDouble()
val back: Int = l.toInt()       // narrowing truncates if out of range
val litLong: Long = 1           // OK: literal adapts to Long

go deeper

for a junior

Knows you must call .toLong() and that a smaller type isn't auto-assignable to a wider one.

for a middle

Explains the no-subtyping rationale and distinguishes literal adaptation from variable widening.

for a senior

Adds operator-overloading behavior for mixed arithmetic and the silent-truncation risk of narrowing.

for a principal

Frames the design tradeoff (safety vs. ergonomics) versus Java and how literal typing keeps the rule ergonomic without breaking it.

## The rule Kotlin has **no implicit widening conversions** between numeric types. In Java, `long l = anInt;` compiles because Java silently widens `int` to `long`. Kotlin rejects the equivalent `val l: Long = anInt` with a type-mismatch error. ## Why In Kotlin the numeric types `Byte`, `Short`, `Int`, `Long`, `Float`, `Double` are **not in a subtype relationship** — `Int` is not a subtype of `Long`. Because there's no subtyping and no implicit conversion, the assignment is simply a type mismatch. The language designers chose this to avoid the silent, sometimes surprising conversions that Java permits (e.g. accidental `int`→`long`→`float` precision loss chains). ## The fix — explicit conversion functions Every numeric type exposes conversion methods: - `toByte()`, `toShort()`, `toInt()`, `toLong()` — integer targets - `toFloat()`, `toDouble()` — floating targets - `toChar()` and `Char.code` for char/int interplay ```kotlin val anInt: Int = 42 // val l: Long = anInt // ERROR: type mismatch val l: Long = anInt.toLong() // OK ``` ## Important exceptions / nuances - **Literals adapt to context.** `val l: Long = 1` compiles: the integer literal `1` is given the expected type `Long` at compile time. There's no variable being widened — the constant is simply typed as `Long`. - **Arithmetic is overloaded, not widening.** `anInt + 1L` returns a `Long` because `Int.plus(Long): Long` is an overloaded operator. The mixed-type operators effectively convert the narrower operand, so you rarely write `toLong()` inside expressions. - **Conversion can lose data.** `toByte()`/`toInt()` truncate; `Long.toInt()` keeps only the low 32 bits. Narrowing is silent — no exception — so be deliberate. ## Summary No automatic widening; call `toLong()`/`toDouble()`/etc. explicitly. Literals and overloaded operators are the two places where it *looks* automatic but isn't variable widening.

  • Why does `val l: Long = 1` compile but `val l: Long = anInt` does not?
    `1` is an integer literal whose type is inferred from the expected context (Long), so it's compiled as a Long constant. `anInt` is already a declared `Int` value, and Int is not assignable to Long without `.toLong()`.
  • Does `anInt + 1L` need an explicit conversion?
    No. The `+` operator is overloaded; `Int.plus(Long)` returns a `Long`, so the narrower operand is handled by the operator and the result is `Long`.

Kotlin treats each numeric type like a different currency: you must explicitly exchange dollars for euros, even though dollars are 'smaller'.

saying these in an interview costs you the question

  • Claiming Int is a subtype of Long
  • Saying Kotlin widens int to long automatically like Java
  • Forgetting that narrowing conversions truncate silently
  • Thinking `val l: Long = 1` proves implicit widening of variables

context