// ── Variables ──
val x: Int = 42 // immutable (preferred)
var y: String = "hello" // mutable
val z = 3.14 // type inference
// ── Basic Types ──
// Byte, Short, Int, Long, Float, Double, Boolean, Char, String
val i: Int = 42
val d: Double = 3.14
val s: String = "hello"
val b: Boolean = true
val c: Char = 'A'
// ── String Templates ──
val name = "World"
"Hello, $name" // "Hello, World"
"1 + 2 = ${1 + 2}" // "1 + 2 = 3"
// ── Null Safety ──
var nullable: String? = null // nullable type
val nonNull: String = "hello" // non-null (compiler enforced)
// Safe call operator
val length = nullable?.length // null if nullable is null
// Elvis operator (null coalescing)
val len = nullable?.length ?: 0 // default if null
// Not-null assertion (dangerous!)
val len2 = nullable!!.length // NPE if null
// Safe cast
val str: String? = obj as? String // null if not String
// Let block on nullable
nullable?.let {
println("Value is $it, length ${it.length}")
}
// Lateinit (for DI)
lateinit var service: UserService
fun init() { service = UserService() }
// ── Type Checks ──
if (obj is String) {
println(obj.length) // smart cast (no cast needed)
}
if (obj !is String) { ... }
// ── Ranges ──
val range = 1..10 // 1 to 10 inclusive
val until = 1 until 10 // 1 to 9
val step = 1..10 step 2 // 1, 3, 5, 7, 9
val down = 10 downTo 1 // 10, 9, 8, ... 1
| Operator | Name | Behavior |
|---|
| ?. | Safe call | Returns null if receiver is null |
| ?: | Elvis | Default value if null |
| !! | Not-null assert | Throws NPE if null |
| as? | Safe cast | Returns null if cast fails |
| .let | Let block | Execute block only if not null |
| Conversion | Method |
|---|
| Int to String | 42.toString() |
| String to Int | "42".toInt() |
| String to Double | "3.14".toDouble() |
| Int to Long | 42.toLong() |
| Double to Int | 3.14.toInt() |
| Any to String | "Value: $x" |
💡Kotlin's null safety is compile-time. String? is nullable, String is not. The compiler prevents NPEs. Use lateinit only when you guarantee initialization before use (e.g., DI frameworks).
// ── Function Declaration ──
fun add(a: Int, b: Int): Int = a + b
fun greet(name: String, greeting: String = "Hello"): String {
return "$greeting, $name!"
}
// Single-expression function
fun double(x: Int) = x * 2
// ── Unit return (void) ──
fun sayHello(): Unit {
println("Hello!")
}
// ── Nothing return (never returns normally) ──
fun fail(message: String): Nothing {
throw IllegalArgumentException(message)
}
// ── Extension Functions ──
fun String.addExclamation(): String = this + "!"
"hello".addExclamation() // "hello!"
fun Int.isEven(): Boolean = this % 2 == 0
4.isEven() // true
// ── Extension Properties ──
val String.firstChar: Char
get() = this[0]
// ── Lambda Functions ──
val sum = { a: Int, b: Int -> a + b }
sum(3, 4) // 7
val double: (Int) -> Int = { it * 2 }
double(5) // 10
// Trailing lambda
val list = listOf(1, 2, 3, 4, 5)
list.filter { it > 3 } // [4, 5]
list.map { it * 2 } // [2, 4, 6, 8, 10]
list.find { it > 3 } // 4
list.any { it > 3 } // true
list.all { it > 0 } // true
list.reduce { acc, n -> acc + n } // 15
list.fold(0) { acc, n -> acc + n } // 15
// ── Infix Functions ──
infix fun Int.power(exp: Int): Long {
return Math.pow(this.toDouble(), exp.toDouble()).toLong()
}
2 power 10 // 1024
// ── Inline Functions ──
inline fun measureTime(block: () -> Unit): Long {
val start = System.currentTimeMillis()
block()
return System.currentTimeMillis() - start
}
val time = measureTime {
// some work
}
// ── List ──
val list = listOf(1, 2, 3) // immutable
val mutable = mutableListOf(1, 2, 3) // mutable
mutable.add(4)
mutable.remove(1)
list + listOf(4, 5) // concat
list.reversed()
list.sorted()
list.sortedDescending()
list.distinct()
list.zip(listOf("a","b","c")) // [(1,"a"), (2,"b"), (3,"c")]
list.flatten()
list.groupBy { if (it % 2 == 0) "even" else "odd" }
// ── Map ──
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
val mutableMap = mutableMapOf("a" to 1)
mutableMap["d"] = 4
mutableMap.remove("a")
map.keys
map.values
map.entries
map.getOrDefault("z", 0)
map.filterKeys { it != "a" }
map.mapValues { it.value * 2 }
map.map { (k, v) -> v to k } // swap
// ── Set ──
val set = setOf(1, 2, 3, 2, 1) // {1, 2, 3}
val mutableSet = mutableSetOf(1, 2)
mutableSet.add(3)
mutableSet.contains(2)
// ── Sequence (lazy evaluation) ──
val seq = sequence {
yield(1)
yieldAll(2..10)
}
seq.take(5).toList() // [1, 2, 3, 4, 5]
// ── Pair and Triple ──
val pair = "name" to "Alice"
val (key, value) = pair
val triple = Triple(1, "hello", true)
val (a, b, c) = triple
Q: What is the difference between val and var?val is read-only (final), var is mutable. Always prefer val. val prevents accidental reassignment and enables safer concurrent code. The compiler may still allow mutation of the object itself (e.g., val list = mutableListOf() — you can modify the list, just not reassign).
Q: How does Kotlin handle null safety?Kotlin distinguishes nullable (T?) and non-null (T) types at compile time. The compiler prevents calling methods on nullable types without safe call (?.) or Elvis (?:) operators. This eliminates NPEs at the language level, unlike Java's @Nullable annotations.
Q: What is the difference between a class and a data class?Data classes auto-generate equals, hashCode, toString, copy, and componentN() based on constructor properties. Regular classes use reference equality. Data classes are ideal for data models. They cannot be open/abstract and have at least one property in primary constructor.
Q: Explain coroutines vs threads.Coroutines are lightweight (not mapped to OS threads) and can be suspended/resumed without blocking. A single thread can run thousands of coroutines. Use Dispatchers.IO for I/O-bound and Dispatchers.Default for CPU-bound work. Structured concurrency ensures proper lifecycle management.
Q: What is a sealed class?Sealed classes restrict inheritance to the same file/module. When used with when expressions, the compiler enforces exhaustive checking. Perfect for modeling a fixed set of states: Result (Success, Error, Loading), network states, or UI states.
💡Top Kotlin interview topics: null safety (operators, smart casts), coroutines (launch, async, Flow, Channels), data classes vs classes, extension functions, sealed classes, scope functions (let, apply, also, run, with), and Kotlin-Java interop.