Basic Types and Variables

This section documents Kotlin 2.4.x on the JVM, as published at kotlinlang.org, which is the reference these pages are written and verified against.

This content was generated with the assistance of AI and should be verified against kotlinlang.org before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

Kotlin is statically typed like Java, but leans heavily on type inference so explicit type annotations are the exception rather than the rule.

val and var

Every declaration is either read-only (val, assignable exactly once) or mutable (var):

val name = "Ada"        // read-only; "name" cannot be reassigned
var count = 0            // mutable
count += 1

val pi: Double = 3.14159 // explicit type annotation (rarely needed -- inferred here anyway)

val is not the same guarantee as Java’s final on a mutable object — a val reference cannot be reassigned, but if it refers to a mutable object (e.g. a MutableList), that object’s contents can still change. Prefer val by default; reach for var only when reassignment is genuinely needed. This preference runs through the whole language — see Data Classes and Destructuring and Collections and Sequences for where it matters most.

Type Inference

The compiler infers a declaration’s type from its initializer, so most val/var declarations carry no explicit type at all — val x: Int = 5 and val x = 5 compile to the identical type. An explicit type is still required for a function parameter, and useful whenever the inferred type would be wider or narrower than intended.

The Built-In Number, Boolean, and Char Types

Type Notes

Byte, Short, Int, Long

8/16/32/64-bit signed integers; integer literals default to Int, suffix L for Long.

Float, Double

32/64-bit IEEE 754 floating-point; literals default to Double, suffix f/F for Float.

Boolean

true/false, no implicit conversion to/from any numeric type.

Char

a single UTF-16 code unit, in single quotes ('A') — not itself a numeric type, unlike Java’s char (explicit conversion via .code/Char(code) is required).

Unlike Java, Kotlin has no separate primitive vs. boxed-wrapper types at the language level — Int is always just Int. The compiler represents it as the JVM primitive int wherever possible and only boxes to java.lang.Integer when a nullable type (Int?) or a generic type parameter forces it — an optimization detail the source code never has to spell out.

val age: Int = 30          // compiles to a JVM primitive int
val maybeAge: Int? = null  // boxed to java.lang.Integer, because null needs a reference type

Any, Unit, and Nothing

Three types sit at the edges of Kotlin’s type hierarchy and have no direct Java equivalent:

  • Any — the root of the non-nullable type hierarchy (Kotlin’s analogue of Object, but every type, including the number types, is a subtype of it — there is no primitive/reference split to work around).

  • Unit — the return type of a function that "returns nothing meaningful," Kotlin’s analogue of void, but it is a real, singleton-valued type — this is what allows a lambda parameter typed () → Unit to be used uniformly wherever a callback with no useful return value is needed.

  • Nothing — the type of an expression that never completes normally (always throws, or is an infinite loop); a function like fun fail(msg: String): Nothing = throw IllegalStateException(msg) lets the compiler treat every branch after a call to it as unreachable, which is what makes ?: throw …​ type-check cleanly in Null Safety's Elvis-operator examples.

Arrays

Array<T> is a fixed-size, mutable, indexable container, distinct from Kotlin’s List/MutableList (covered in Collections and Sequences):

val letters = arrayOf("a", "b", "c")
val zeros = IntArray(5)                 // primitive-backed, no boxing: [0, 0, 0, 0, 0]
val squares = IntArray(5) { it * it }   // lambda-initialized: [0, 1, 4, 9, 16]

println(letters[1])                      // "b"
letters[1] = "B"

Dedicated primitive-array types (IntArray, DoubleArray, BooleanArray, …​) avoid boxing overhead, mirroring Java’s int[]/double[]/etc.; Array<Int> (boxed) also exists but is rarely what you want for numeric data.

Unsigned Integer Types

Kotlin adds unsigned variants with no Java equivalent — UByte, UShort, UInt, ULong — for values that are conceptually never negative (sizes, bit flags, hash codes read from an external format):

val port: UInt = 8080u
val fileSize: ULong = 4_294_967_296uL

They are represented, like the signed types, as their same-width JVM primitive at runtime, with the sign reinterpreted — there is no wider "real" unsigned JVM type underneath.

See Also