skip to content

Type Inference

Kotlin infers a local or property type from its initializer, so val n = 1 is an Int unless you annotate otherwise. Knowing when the annotation becomes mandatory — uninitialized declarations, recursive functions, widening to Long — is the practical half of this topic.

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

questions

5

What is type inference in Kotlin, and how does the compiler decide the type of `val n = 1`?

level: juniorimportance: must knowfreq 80%

answer

  1. Type comes from the right-hand initializer
  2. Integer literal default = Int
  3. Static + strong typing, only the annotation is omitted
  4. val n = 1 == val n: Int = 1
  5. Most-specific suitable type

basics

~20 s

Type inference lets you skip writing the type. The compiler reads the value on the right and figures out the type itself. For val n = 1 it sees a whole number, so n is an Int.

solid answer

~40 s

Type inference means the compiler derives a variable's static type from its initializer expression instead of you annotating it. For `val n = 1`, the literal `1` is an integer literal whose default type is `Int`, so `n` is inferred as `Int`. Inference is purely compile-time: Kotlin stays statically and strongly typed, so `n` is exactly as type-safe as `val n: Int = 1`. It works for local variables and for properties with an initializer. The inferred type is the most specific suitable type the compiler can determine from the right-hand side: `"hi"` gives `String`, `1.0` gives `Double`, `listOf(1, 2)` gives `List<Int>`. You can always override inference with an explicit annotation, e.g. `val n: Long = 1` or `val n: Number = 1`, when you want a wider or different type.

code

kotlin · 5 lines
kotlin
val n = 1          // Int
val price = 9.99   // Double
val name = "Ada"   // String
val ids = listOf(1, 2, 3) // List<Int>
// name = 5 // error: name is String

go deeper

for a junior

Knows that the type comes from the value and that val n = 1 is an Int.

for a middle

Explains it's compile-time, that Kotlin stays statically/strongly typed, and lists default literal types.

for a senior

Frames inference as picking the most-specific suitable type and notes equivalence to explicit annotation in bytecode/safety.

for a principal

Discusses inference as a readability/maintainability lever and the trade-offs of relying on it in public APIs vs locals.

## What type inference is **Type inference** is the compiler's ability to determine the static type of a declaration from the **initializer** (the expression on the right of `=`) so you don't have to write the type yourself. Kotlin is **statically typed** (every expression has a type known at compile time) and **strongly typed** (types are enforced); inference only removes the *typing*, never the *checking*. ```kotlin val n = 1 // inferred Int — identical to: val n: Int = 1 val s = "hi" // inferred String val d = 1.0 // inferred Double val flag = true // inferred Boolean ``` ## How `val n = 1` becomes Int - `1` is an **integer literal**. An undecorated integer literal that fits in 32 bits has the **default type `Int`**. - The compiler picks the most specific type that fits the expression, so `n` is `Int`, not `Number` or `Long`. - After inference, `n` behaves exactly like an explicitly typed `Int`; you get the same autocomplete, the same errors, the same generated bytecode. ## Default literal types worth remembering - Integer literal → `Int` (or `Long` if it doesn't fit in `Int`, or with the `L` suffix: `1L`). - Floating literal → `Double` (or `Float` with the `f`/`F` suffix: `1.0f`). - `'a'` → `Char`; `"a"` → `String`; `true`/`false` → `Boolean`. ## Where inference applies — and where it doesn't - **Applies:** local `val`/`var` with an initializer, and properties with an initializer. - **Does NOT apply / annotation required:** an uninitialized `lateinit`/abstract property, a property/parameter with no initializer, and (for clarity/recursion) certain function return types. More on that in follow-ups. ## Inference keeps you type-safe ```kotlin val n = 1 // n = "oops" // compile error: n is Int, cannot assign String ``` The takeaway: omitting the type is a convenience, not a loss of safety — the type is still fixed at compile time.

  • Does inference make Kotlin dynamically typed like Python?
    No. The type is fixed at compile time; inference only omits the annotation. Kotlin stays statically and strongly typed.
  • What type is `val x = 1.0`?
    `Double` — an undecorated floating-point literal defaults to `Double`, not `Float`.

Like a tailor sizing a suit from the person standing in front of them instead of asking for measurements — the fit is exact, you just didn't have to state the numbers.

saying these in an interview costs you the question

  • Claiming inference makes Kotlin dynamically typed
  • Saying the type can change at runtime
  • Thinking `val n = 1` is `Long` or `Number` by default
  • Believing inference is slower at runtime (it's compile-time only)

context

open as a page

When does Kotlin REQUIRE you to write an explicit type instead of relying on inference?

level: middleimportance: must knowfreq 70%

basics

~10 s

When there's no value to infer from. If a variable or property has no initializer yet, or the compiler can't work out the type on its own, you must write the type explicitly.

open as a page

Why is `val x = 1` inferred as `Int` but you might need `val x: Long = 1`? Explain integer literal types and how to force `Long`.

level: middleimportance: should knowfreq 55%

basics

~20 s

A plain number like 1 is treated as an Int by default. If you need a Long, either write the type (val x: Long = 1) or add an L suffix (1L). The value is the same; the type differs.

open as a page

How does Kotlin infer the type of an expression like `if`/`when` branches or `listOf(1, "a")`, and what subtle pitfalls (e.g. widening to `Any`, platform types, generic inference) should you watch for?

level: seniorimportance: should knowfreq 45%

basics

~10 s

When values have different types, Kotlin picks the nearest common parent type. Mixing an Int and a String gives Any. Watch out: the inferred type can be wider than you want, hiding bugs.

open as a page

How does return-type inference work for functions and properties, and when should you annotate the return type even though the compiler could infer it?

level: seniorimportance: should knowfreq 40%

basics

~20 s

A function written with = (an expression body) can have its return type guessed from the result. Block functions with { } cannot. Even when inference works, write the return type for public functions so callers aren't surprised by refactors.

open as a page