Kotlin Language: Variables & Types Deep Dive (Part 1/2)
A detailed learning-in-public deep dive on Variables & Types in Kotlin Language, covering concept batch 1/2.
Table of contents
- Context and Scope
- Conceptual Model & Analogy
- Deep Dive
- 1) val — read-only reference (must know)
- 2) var — mutable reference (must know)
- 3) Type Inference (must know)
- 4) String — immutable text with raw/triple-quoted literals (must know)
- 5) String Templates — inline interpolation (must know)
- 6) Int / Long / Double / Float — numeric primitives with Kotlin ergonomics (must know)
- 7) Boolean — true/false with lazy operators
- 8) Char — a single character (not a one-character String)
- Implementation Patterns
- Common Pitfalls and Tradeoffs
- Production-grade example (combines all eight concepts)
- Technical Note
- Sources & Further Reading
- Check Your Work
- Hands-on Exercise
- Brain Teaser
Context and Scope
This is the first deep-dive in the Kotlin Language: Variables & Types series (Fundamental level). We’ll cover eight must-know concepts you’ll use in every Kotlin file you write:
- val
- var
- Type inference
- String
- String templates
- Int / Long / Double / Float
- Boolean
- Char
The guidance here reflects current Kotlin documentation and behavior on Kotlin 2.x (K2 frontend), which has strengthened type and nullability inference in recent releases. Run date: 2026-03-04. (kotlinlang.org)
Conceptual Model & Analogy
Think of a variable as a labeled box sitting on your desk:
- val is a sealed shipping box: you can put one thing in once (at initialization), then you can’t replace that thing. If the thing itself is mutable (say, a toolbox), its contents can still change.
- var is a resealable box: you can swap what’s inside as often as you need.
Kotlin’s type system is the warehouse inventory: it keeps track of what’s inside each box. Often, Kotlin can infer the inventory label from the item you place in the box (type inference). (kotlinlang.org)
Deep Dive
1) val — read-only reference (must know)
- Meaning: assign once; no reassignment. Local vals and read-only properties expose only a getter; they don’t imply deep immutability of the object they reference. (kotlinlang.org)
- When to prefer: default to val for local variables and properties; it communicates intent and limits incidental mutation (a frequent source of bugs). Kotlin’s coding conventions explicitly recommend val when possible. (kotlinlang.org)
- Common use cases: dependency references, computed results, configuration snapshots.
Baseline example:
fun main() {
val greeting = "Hello" // inferred as String
// greeting = "Hi" // ❌ compile error: val cannot be reassigned
val ports = mutableListOf(8080) // val reference to a mutable object
ports += 9090 // ✅ object mutates; the val itself is unchanged
println("$greeting, ports = $ports")
}
2) var — mutable reference (must know)
- Meaning: can be reassigned any number of times. Properties declared with var have a getter and a setter. (kotlinlang.org)
- When to prefer: values that evolve over time (counters, caches, iteratively computed results), or properties with a legitimate notion of state change.
var attempts = 0
attempts++ // ✅
3) Type Inference (must know)
- Kotlin infers a variable’s type from its initializer, so
val count = 0becomesInt, andval pi = 3.14becomesDouble. You can omit explicit types for locals unless clarity demands otherwise (e.g., public APIs). (kotlinlang.org) - K2 (Kotlin 2.x) strengthened flow and nullability inference in edge cases (notably with Java interop and control flow), correcting scenarios where earlier compilers inferred too-optimistic types. If you’re migrating from 1.9.x, re-check warnings and nullable types surfaced by K2. (kotlinlang.org)
val users = listOf("Ada", "Linus") // inferred as List<String>
val threshold = 1_000L // inferred as Long because of the literal and suffix
4) String — immutable text with raw/triple-quoted literals (must know)
- Kotlin’s String is immutable: operations return new strings. Kotlin supports both escaped strings and raw (triple-quoted) multiline strings. Use trimMargin() or trimIndent() to clean indentation in raw blocks. (kotlinlang.org)
- Practical note: On the JVM, String data uses UTF-16 under the hood. (kotlinlang.org)
val banner = """
|Welcome
|to Kotlin
""".trimMargin() // "Welcome\nto Kotlin"
5) String Templates — inline interpolation (must know)
- Embed values with $variable or ${expression}, which calls toString() automatically. Prefer templates over concatenation for readability. (kotlinlang.org)
- Need literal $ in a raw string? Use ${’$’}. Recent docs also describe an experimental “multi-dollar string interpolation” for complex cases; keep to the standard single-$ unless you’ve explicitly opted in. (kotlinlang.org)
val name = "Kotlin"
println("Hello, $name! Length = ${name.length}")
6) Int / Long / Double / Float — numeric primitives with Kotlin ergonomics (must know)
- Integers: Byte, Short, Int, Long. Floating-point: Float, Double (IEEE 754). Numeric literals support underscores, hex, binary, and
L/fsuffixes. No octal. (kotlinlang.org) - On the JVM, non-null numeric types compile to primitives; nullable or generic contexts box them. Be mindful of identity (===) vs structural (==) equality with boxed numbers; use == for numeric comparisons. (kotlinlang.org)
- No implicit widening: convert explicitly with toInt(), toLong(), etc.; integer division truncates. (kotlinlang.org)
val i = 1 // Int
val big = 3_000_000_000 // Long (inferred; exceeds Int range)
val ratio = 5 / 2 // Int: 2
val precise = 5 / 2.0 // Double: 2.5
7) Boolean — true/false with lazy operators
- Boolean supports ||, &&, ! with short-circuit (lazy) evaluation. Useful to avoid expensive computations in the second operand. (kotlinlang.org)
- On the JVM, nullable Boolean is boxed just like numbers; compare with ==, not ===. (kotlinlang.org)
fun shouldProceed(flag: Boolean, expensive: () -> Boolean) =
flag && expensive() // expensive() not called if flag is false
8) Char — a single character (not a one-character String)
- Char literals use single quotes: ‘A’. On the JVM, Char is a 16-bit UTF-16 code unit; some Unicode glyphs (like many emoji) require surrogate pairs, so iterating a String by Char may split a visible glyph into two units. Convert between Char and String with toString(); numeric conversions use dedicated APIs (for digits, use digitToInt()). (kotlinlang.org)
val ch: Char = '7'
val d = ch.digitToInt() // 7
val oneChar: String = ch.toString()
Implementation Patterns
- Prefer val by default:
- Local variables and properties should be val unless mutation is essential. Improves reasoning, enables more smart casts, and signals intent. (kotlinlang.org)
- Make types explicit at your API boundaries:
- Rely on inference for locals; explicitly annotate public function return types and public properties to avoid accidental API drift.
- Use compile-time constants for stable configuration:
- const val at top-level or inside objects/companion objects for primitive and String constants (inlined at call sites). Good for annotation arguments and feature flags. (kotlinlang.org)
- Favor string templates and raw strings:
- Templates boost readability; raw strings avoid backslash soup. Use trimMargin/trimIndent to keep indentation clean. (kotlinlang.org)
- Be explicit with numbers:
- Convert intentionally (toInt/toLong/etc.), watch integer division, and compare with == (not ===). Suffix literals with L or f as needed. (kotlinlang.org)
- Keep boolean logic cheap:
- Leverage short-circuiting to guard expensive work. (kotlinlang.org)
- Remember Char vs String:
- Char is not a one-character String and on JVM represents a UTF-16 code unit; use higher-level libraries when you need grapheme-aware processing across platforms. (kotlinlang.org)
Common Pitfalls and Tradeoffs
- “val means immutable” — not quite:
- val forbids reassignment of the reference, not mutation of the referenced object. If you need deep immutability, expose immutable types or copy defensively. (kotlinlang.org)
- Overusing var:
- Widespread mutability complicates reasoning and concurrency. Start with val; relax to var where evidence demands it. (kotlinlang.org)
- Inference that hides intent:
- Locals are fine to infer. For public APIs, explicit types prevent surprises during refactors/migrations.
- Numeric traps:
- Integer division truncates; floating comparisons are IEEE 754 when statically typed as Float/Double; == vs === behaves differently for boxed numbers. (kotlinlang.org)
- String-template dollars:
- In multiline strings, ${’$’} is the portable way to insert a literal $. The new “multi-dollar interpolation” is experimental; don’t adopt it unless you’ve opted in and tested across targets. (kotlinlang.org)
- Char and Unicode:
- Iterating a String by Char can split a single visible glyph into multiple code units. Account for this in parsing/validation. (kotlinlang.org)
Production-grade example (combines all eight concepts)
// File: AppConfig.kt
package example
// Compile-time constants (JVM/MPP-safe where used appropriately)
const val DEFAULT_HOST: String = "localhost"
const val DEFAULT_PORT: Int = 8080
data class ServerConfig(
val host: String, // val: read-only property
val port: Int, // numeric primitive
val useSsl: Boolean, // boolean
val banner: String = """ // raw multiline String
|Welcome to $host:$port
|SSL: $useSsl
""".trimMargin()
)
class ConfigLoader {
// Mutable working state during parsing
private var attempts: Int = 0 // var: mutable
fun loadFromEnv(env: Map<String, String>): ServerConfig {
attempts++
// Type inference for locals
val host = env["HOST"] ?: DEFAULT_HOST
// Explicit conversion; no implicit widening
val port: Int = env["PORT"]?.toIntOrNull() ?: DEFAULT_PORT
val useSsl = (env["USE_SSL"] ?: "false").toBooleanStrictOrNull() ?: false
// String templates; Char vs String awareness
val heart: Char = '❤'
val banner = """
|${heart} ${"Connected to $host on port $port (ssl=$useSsl)".uppercase()}
""".trimMargin()
return ServerConfig(host = host, port = port, useSsl = useSsl, banner = banner)
}
}
fun main() {
val env = mapOf("HOST" to "kotlinlang.org", "PORT" to "443", "USE_SSL" to "true")
val loader = ConfigLoader()
val cfg = loader.loadFromEnv(env)
println(cfg.banner)
// Boxing/equality footnote: prefer structural equality
val a: Int = 100
val x: Int? = a
val y: Int? = a
println(x == y) // true (value equality)
// println(x === y) // discouraged with boxable numbers
}
References embedded:
- const val and inlining: Properties docs. (kotlinlang.org)
- Type inference, val/var semantics: Basic syntax. (kotlinlang.org)
- Strings, raw literals, templates: Strings docs. (kotlinlang.org)
- Numbers, conversions, equality cautions: Numbers docs. (kotlinlang.org)
- Boolean short-circuiting: Booleans docs. (kotlinlang.org)
- Char usage: Characters docs. (kotlinlang.org)
Technical Note
If you’re moving code from Kotlin 1.9.x to 2.x (K2), you may see behavior differences in type and nullability inference, especially around Java interop and control flow (try/catch, smart casts). These are intentional fixes; adjust annotations and nullable types accordingly. Run your test suite and address newly surfaced warnings instead of suppressing them. (kotlinlang.org)
Sources & Further Reading
- Kotlin docs: Basic syntax (variables, type inference, templates) — https://kotlinlang.org/docs/basic-syntax.html (kotlinlang.org)
- Kotlin docs: Types overview (primitives optimized at runtime, object semantics) — https://kotlinlang.org/docs/types-overview.html (kotlinlang.org)
- Kotlin docs: Numbers (literals, conversions, boxing/caching, IEEE 754 details) — https://kotlinlang.org/docs/numbers.html (kotlinlang.org)
- Kotlin docs: Strings (immutability, raw/triple-quoted, templates) — https://kotlinlang.org/docs/strings.html (kotlinlang.org)
- Kotlin docs: Booleans (operators, lazy evaluation) — https://kotlinlang.org/docs/booleans.html (kotlinlang.org)
- Kotlin docs: Characters (Char basics, JVM representation) — https://kotlinlang.org/docs/characters.html (kotlinlang.org)
- Kotlin docs: Coding conventions (prefer val, naming, template braces guidance) — https://kotlinlang.org/docs/coding-conventions.html (kotlinlang.org)
- Kotlin docs: Properties (getters/setters; const val, inlining) — https://kotlinlang.org/docs/properties.html (kotlinlang.org)
- Kotlin stdlib: trimIndent/trimMargin — https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.text/trim-indent.html and https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.text/trim-margin.html (kotlinlang.org)
- K2 compiler migration guide (notable inference fixes) — https://kotlinlang.org/docs/k2-compiler-migration-guide.html (kotlinlang.org)
Check Your Work
Hands-on Exercise
- Rewrite a small Java-style snippet into idiomatic Kotlin:
- Replace concatenation with string templates.
- Replace mutable locals with val where possible.
- Add explicit types only for public declarations.
- Parse configuration:
- Given a Map<String, String>, build a data class with fields host (String), port (Int), secure (Boolean), and banner (multiline String). Use type inference for locals, explicit conversion for numbers, and template expressions for the banner.
- Numeric sanity checks:
- Demonstrate integer division truncation and fix it with conversions.
- Compare two boxed Int? values both with
==and===and note the difference.
- Char vs String:
- Given a string containing emoji, iterate by Char and show how length differs from the number of visible glyphs. Explain why.
Brain Teaser
- What does this print, and why?
val a = 1_000
val b: Int? = a
val c: Int? = a
println(b == c)
println(b === c)
- Given:
val s = """
|$5.00
|${'$'}5.00
""".trimMargin()
What are the two lines of output, and how would you embed a literal $ at the start of a line inside a raw string without triggering interpolation?
- True or false (and justify):
- “val variables are immutable values.”
- “Kotlin implicitly widens smaller numeric types to larger ones.”
- “A Char is the same as a one-character String.”
Share
More to explore
Keep exploring
3/4/2026
Weekly Engineering Mastery Quiz (2026-03-02 to 2026-03-06)
A 20-question assessment covering fundamentals, implementation best practices, and advanced architecture insights from this week’s learning posts.
3/4/2026
Kotlin Language: Variables & Types Deep Dive (Part 2/2)
A detailed learning-in-public deep dive on Variables & Types in Kotlin Language, covering concept batch 2/2.
1/11/2026
Kotlin to C/C++ Transition Guide: A Systems Programmer's Cheat Sheet
Preparing for a systems programming interview but haven't touched C/C++ since university? This guide bridges your Kotlin knowledge to C/C++ with side-by-side syntax comparisons, memory management deep dives, and critical undefined behaviors you need to know.
Next