Types and variables
val, var, and inference
Use val for a read-only reference and var when the reference must be reassigned.
val userId: Long = 42L
val displayName = "Rakesh" // inferred String
var retryCount = 0 // inferred Int
retryCount += 1
Prefer val: fewer changing references mean fewer possible program states. Use var only when reassignment represents a real transition.
val does not mean immutable
val names = mutableListOf("Ada")
names += "Grace" // object changes
// names = mutableListOf("Linus") // reference cannot change
val protects the reference, not the referenced object's internal state. For true immutability, the object's API must prevent observable mutation.
Common Kotlin types
| Type | Example | Detail |
|---|---|---|
Int | 42 | 32-bit signed integer |
Long | 42L | 64-bit; suffix matters |
Float | 3.5F | 32-bit floating point |
Double | 3.5 | Default decimal type |
Boolean | true | Conditions must be explicitly true or false; 0 and 1 are not Boolean values |
Char | 'A' | Not a String |
String | "Android" | Immutable character sequence |
In Kotlin code, these all look and behave like normal types. Behind the scenes, the JVM can store many non-null numbers and booleans as efficient primitive values. You usually do not need to think about that optimization while writing ordinary application code.
Why numbers are not booleans
Kotlin requires every condition to produce a Boolean:
val count = 1
// if (count) { } // Compilation error
if (count > 0) {
println("At least one item")
}
Kotlin does not guess that 0 means false or that a nonzero number means true. You must write the rule you actually mean: count > 0, value != 0, or value == 1.
If a database or old API uses 1 and 0, convert it explicitly:
val databaseValue = 1
val isEnabled: Boolean = databaseValue == 1
Kotlin requires explicit numeric conversion
An Int and a Long are different types. Although every Int value fits inside a Long, Kotlin does not silently widen an Int during assignment.
val count: Int = 10
// val total: Long = count // Compilation error
val total: Long = count.toLong()
count.toLong() creates the Long value 10L. In simple terms: Kotlin makes you say when you want a different numeric type instead of silently guessing.
Mixed arithmetic is different
Kotlin can select an arithmetic operation based on both operands:
val count: Int = 10
val price: Long = 100L
val total = count + price
println(total) // 110
Here, total is a Long because the addition includes a Long. This is a new arithmetic result; it is not a direct assignment of the original Int to a Long variable.
Arithmetic overflow is a separate problem
Conversion and overflow are different:
- Conversion: changing a value from one numeric type to another, such as
InttoLong. - Overflow: producing a number that is too large or too small for the type doing the calculation.
The largest Int is 2,147,483,647. Adding one exceeds the Int range:
val maximum = Int.MAX_VALUE
val overflowed = maximum + 1
println(maximum) // 2147483647
println(overflowed) // -2147483648
On the JVM, ordinary Int arithmetic wraps around instead of throwing automatically.
Convert before the calculation
val safe: Long = Int.MAX_VALUE.toLong() + 1
println(safe) // 2147483648
The conversion happens first, so addition uses Long arithmetic and this result fits.
Converting afterward is too late:
val wrong: Long = (Int.MAX_VALUE + 1).toLong()
println(wrong) // -2147483648
The calculation inside the parentheses happens first. Both operands are Int, so the result overflows as an Int. Calling .toLong() afterward only converts the already-wrong value.
Counts, file sizes, time durations, prices in minor units, and pagination offsets may originate outside your code. Validate their permitted ranges and perform calculations in a sufficiently wide type. For calculations where overflow must fail rather than wrap, use checked operations such as Java's Math.addExact and handle ArithmeticException.
Kotlin does not automatically assign an Int to a Long; use toLong(). Conversion order matters: if an Int calculation overflows first, converting its result to Long cannot repair it. Convert an operand before the calculation when the result needs Long range.
Public API guidance
For short local code, inferred types are usually easy to read. For public functions and properties, writing the type explicitly often makes the API clearer and prevents an internal edit from unexpectedly changing what callers receive.
Open in Kotlin PlaygroundQuick check
Can the contents referenced by a val change?
Yes, if the referenced object exposes mutation. Only reassignment of the variable is prohibited.