Types and Variables
val and var
Section titled “val and var”val declares a read-only variable (assigned once). var declares a mutable variable.
val immutable: String = "assigned once"var mutable: String = "can be reassigned"mutable = "new value"// immutable = "error" // Val cannot be reassignedval does not mean the object is immutable — it means the reference cannot be reassigned. The Underlying object may still be mutable.
val list = mutableListOf(1, 2, 3)list.add(4) // compiles: mutating the object, not the reference// list = mutableListOf(5, 6) // error: val cannot be reassignedPrefer val everywhere. Use var only when the variable genuinely must be reassigned.
Basic Types
Section titled “Basic Types”Kotlin represents all types as objects at the language level. The compiler maps them to JVM Primitives when possible (no boxing overhead).
| Kotlin Type | JVM Type | Size (bits) |
|---|---|---|
Byte | byte | 8 |
Short | short | 16 |
Int | int | 32 |
Long | long | 64 |
Float | float | 32 |
Double | double | 64 |
Boolean | boolean | 1 |
Char | char | 16 |
Numeric literals support underscores for readability:
val million = 1_000_000val hex = 0xFF_EC_DE_5Eval binary = 0b1010_1010val longVal = 42Lval doubleVal = 3.14val floatVal = 3.14fImplicit Conversions
Section titled “Implicit Conversions”Kotlin does not perform implicit widening conversions. Every conversion is explicit.
val intVal: Int = 42val longVal: Long = intVal.toLong() // explicitval doubleVal: Double = intVal.toDouble()// val bad: Long = intVal // compile errorThe toXxx() methods exist on all numeric types: toByte()``toShort()``toInt()``toLong() toFloat()``toDouble()``toChar().
Type Inference
Section titled “Type Inference”The compiler infers the type from the initializer when the type is unambiguous.
val name = "Kotlin" // inferred: Stringval count = 42 // inferred: Intval price = 9.99 // inferred: Doubleval flag = true // inferred: Booleanval items = listOf(1, 2) // inferred: List<Int>Type inference does not make Kotlin dynamically typed. The inferred type is concrete and enforced at Compile time.
var x = 42 // inferred: Int// x = "string" // error: type mismatchUse explicit types when the inferred type is not obvious or when the type carries important semantic Information.
val users: Map<Long, String> = emptyMap()val response: Result<Data> = fetchFromNetwork()Strings
Section titled “Strings”Strings are immutable. Kotlin supports string templates and multiline strings.
val name = "World"val greeting = "Hello, $name!"val expr = "2 + 2 = ${2 + 2}"
val json = """ { "name": "$name", "value": ${42} }""".trimIndent()trimIndent() removes common leading whitespace. trimMargin() uses a custom margin prefix:
val text = """ |Line 1 |Line 2 |Line 3""".trimMargin()Raw strings (triple-quoted) do not support escape sequences. Use ${"$'} to insert a literal dollar Sign.
Nullable Types and Null Safety
Section titled “Nullable Types and Null Safety”This is the defining feature of Kotlin’s type system. The type String is non-nullable; String? Is nullable. The compiler prevents nullable values from being used where non-nullable values are Expected.
var nonNull: String = "always has a value"// nonNull = null // compile error
var nullable: String? = "might be null"nullable = null // OK
val len: Int = nonNull.length // compile error: nullable receiverval len2: Int? = nullable?.length // OK: safe call, returns null if nullable is nullSafe Call Operator: ?.
Section titled “Safe Call Operator: ?.”Chains safely through potentially null references. Returns null if any receiver in the chain is Null.
val city: String? = user?.address?.city// city is String? -- null if user, address, or city is nullElvis Operator: ?:
Section titled “Elvis Operator: ?:”Provides a default value when the left side is null.
val name: String = nullableName ?: "Unknown"val length: Int = nullable?.length ?: 0Not-Null Assertion: !!
Section titled “Not-Null Assertion: !!”Throws KotlinNullPointerException if the value is null. Use sparingly — it bypasses the null Safety system.
val name: String = nullableName!! // throws if nullSafe Cast: as?
Section titled “Safe Cast: as?”Returns null instead of throwing ClassCastException.
val str: String? = obj as? String // null if obj is not a StringLate-Initialized Properties
Section titled “Late-Initialized Properties”Use lateinit for non-nullable properties that cannot be initialized in the constructor (dependency Injection, framework callbacks).
class DatabaseService { lateinit var connection: Connection
fun init() { connection = DriverManager.getConnection(url) }
fun query(sql: String): ResultSet { return connection.createStatement().executeQuery(sql) }}lateinit has tradeoffs:
- Accessing before initialization throws
UninitializedPropertyAccessException. - Only works with
varand non-primitive types. - You can check initialization with
::connection.isInitialized.
Nullable Collections vs Collections of Nullable Elements
Section titled “Nullable Collections vs Collections of Nullable Elements”val nullableList: List<Int>? = listOf(1, 2, 3) // the list itself might be nullval listOfNullables: List<Int?> = listOf(1, null, 3) // the list contains nullable elements
val result: Int? = nullableList?.firstOrNull()?.plus(1)Smart Casts
Section titled “Smart Casts”The compiler tracks null checks and type checks, automatically casting within the checked scope.
fun processValue(value: Any) { if (value is String) { // value is smart-cast to String here println(value.uppercase()) println(value.length) }
if (value is Int && value > 0) { // value is smart-cast to Int println(value * 2) }
when (value) { is Double -> println(value.toBigDecimal()) is List<*> -> println(value.size) }}Smart casts work when the compiler can prove the variable cannot change between the check and usage. This means the variable must be val (or effectively final var) and not a custom property getter.
Type Aliases
Section titled “Type Aliases”Type aliases create alternative names for existing types. They do not create new types.
typealias UserId = Longtypealias UserName = Stringtypealias UserMap = Map<UserId, UserName>
fun lookup(id: UserId): UserName? { return users[id]}Type aliases are useful for domain modeling and reducing verbosity in complex generic signatures.
Common Pitfalls
Section titled “Common Pitfalls”- ** Using
!!liberally. Each!!is a potential runtime crash. Prefer safe calls, Elvis operator, or early returns. - ** Forgetting that
valdoes not imply immutability of the referenced object.val list = mutableListOf(1, 2)is a mutable list behind a read-only reference. - ** Confusing nullable collections with collections of nullable elements.
List<Int>?vsList<Int?>are fundamentally different types. - ** Using
lateinitfor properties that can be initialized in the constructor. If the value is known at construction time, pass it as a constructor parameter.
Intuition
Section titled “Intuition”Kotlin’s type system is designed to eliminate null reference errors at compile time. Non-nullable types are the default, and nullable types require explicit handling through safe calls, Elvis operators, or not-null assertions. Type inference lets the compiler deduce types from context, keeping code concise while maintaining safety. Smart casts reduce boilerplate by automatically casting values after a type check. Together, these features make Kotlin’s type system both expressive and safe.
Summary
Section titled “Summary”This topic covers the core concepts of types and variables, including underlying theory, practical implementation, and key applications.
Key concepts include:
- core concepts and terminology
- algorithms and computational thinking
- practical implementation
- security and ethical considerations
- applications in the real world
Understanding these concepts thoroughly is essential for both examinations and practical programming, and requires both theoretical knowledge and hands-on practice.
Worked Examples
Section titled “Worked Examples”Worked examples demonstrating the application of key concepts are covered in the detailed sub-pages linked above.
Cross-References
Section titled “Cross-References”- Control Flow: If-expressions, when, and loops that use the types defined here.
- Null Safety Deep Dive: Nullable type specifiers and safe-call operators.
- Kotlin Practice: Auto-graded problems testing type system fundamentals.