Top 100 Kotlin Interview Questions: Complete Guide for Developers
Master Kotlin interview questions covering null safety, coroutines, collections, OOP, functional programming, JVM internals, and Android development with detailed answers and code examples.
Top 100 Kotlin Interview Questions: Complete Guide for Developers
📋 Table of Contents
- What is Kotlin Interview Preparation?
- Why Do We Need This Guide?
- Real-World Use Cases
- Question Categories
- Core Concepts Deep Dive
- Memory and Performance
- Architecture Patterns
- Interview Questions with Answers
- Common Mistakes
- Performance Considerations
- Advanced Interview Questions
- Cheat Sheet
- Summary
1. What is Kotlin Interview Preparation?
Kotlin is a modern, statically-typed programming language developed by JetBrains that runs on the JVM. It's officially supported for Android development and increasingly popular for backend services.
Why Kotlin is Important
┌─────────────────────────────────────────────────────────────┐
│ KOTLIN ADOPTION │
├─────────────────────────────────────────────────────────────┤
│ │
│ Android Development │
│ ─────────────────── │
│ • Official language (Google, 2017) │
│ • 60%+ of top 1000 Android apps │
│ • Preferred over Java │
│ │
│ Backend Development │
│ ─────────────────── │
│ • Spring Boot support │
│ • Ktor framework │
│ • Microservices │
│ │
│ Companies Using Kotlin │
│ ───────────────────── │
│ • Google • Netflix • Uber │
│ • Pinterest • Trello • Slack │
│ • Square • Coursera • Basecamp │
│ │
└─────────────────────────────────────────────────────────────┘
Key Features
┌─────────────────────────────────────────────────────────────┐
│ KOTLIN KEY FEATURES │
└─────────────────────────────────────────────────────────────┘
1. Null Safety
• Eliminates NullPointerException
• Nullable types (String?)
• Safe call operator (?.)
• Elvis operator (?:)
2. Concise Syntax
• Less boilerplate than Java
• Data classes
• Extension functions
• Smart casts
3. Functional Programming
• Higher-order functions
• Lambda expressions
• Immutability support
• Collection operations
4. Coroutines
• Lightweight threads
• Async/await pattern
• Structured concurrency
• Flow for reactive streams
5. Java Interoperability
• 100% compatible with Java
• Call Java from Kotlin
• Call Kotlin from Java
• Gradual migration
6. Modern Language Features
• Type inference
• Sealed classes
• Delegation
• Operator overloading
2. Why Do We Need This Guide?
Interview Preparation Challenges
┌─────────────────────────────────────────────────────────────┐
│ COMMON INTERVIEW CHALLENGES │
├─────────────────────────────────────────────────────────────┤
│ │
│ Without Proper Preparation: │
│ ────────────────────────── │
│ ❌ Scattered resources │
│ ❌ Incomplete coverage │
│ ❌ No practical examples │
│ ❌ Missing advanced topics │
│ ❌ No real-world scenarios │
│ │
│ With This Guide: │
│ ─────────────── │
│ ✅ 100 curated questions │
│ ✅ Detailed explanations │
│ ✅ Code examples │
│ ✅ Best practices │
│ ✅ Real-world applications │
│ ✅ Performance tips │
│ │
└─────────────────────────────────────────────────────────────┘
Target Roles
- Android Developer (Junior to Senior)
- Backend Developer (Kotlin/Spring Boot)
- Full Stack Developer (Kotlin + React/Vue)
- Mobile Architect
- Technical Lead
3. Real-World Use Cases
1. Android App Development
// Modern Android with Kotlin
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Coroutines for async operations
lifecycleScope.launch {
viewModel.userFlow.collect { user ->
updateUI(user)
}
}
}
}
2. Backend Microservices
// Spring Boot REST API
@RestController
@RequestMapping("/api/users")
class UserController(
private val userService: UserService
) {
@GetMapping("/{id}")
suspend fun getUser(@PathVariable id: Long): User {
return userService.findById(id)
?: throw UserNotFoundException(id)
}
@PostMapping
suspend fun createUser(@RequestBody user: User): User {
return userService.save(user)
}
}
3. Data Processing Pipeline
// Functional data processing
fun processOrders(orders: List<Order>): OrderSummary {
return orders
.filter { it.status == OrderStatus.COMPLETED }
.groupBy { it.customerId }
.mapValues { (_, orders) ->
orders.sumOf { it.total }
}
.let { OrderSummary(it) }
}
4. Question Categories
Interview Question Distribution
┌─────────────────────────────────────────────────────────────┐
│ 100 QUESTIONS BREAKDOWN │
└─────────────────────────────────────────────────────────────┘
Category Questions Difficulty
────────────────────────────────────────────────────────────
Basics & Syntax 15 ⭐ Beginner
Null Safety 10 ⭐⭐ Intermediate
Collections 10 ⭐⭐ Intermediate
OOP & Classes 12 ⭐⭐ Intermediate
Functional Programming 10 ⭐⭐⭐ Advanced
Coroutines & Concurrency 15 ⭐⭐⭐ Advanced
Extension Functions 8 ⭐⭐ Intermediate
Sealed Classes & Enums 5 ⭐⭐ Intermediate
Delegation 5 ⭐⭐⭐ Advanced
JVM Internals 5 ⭐⭐⭐ Advanced
Android Specific 5 ⭐⭐ Intermediate
────────────────────────────────────────────────────────────
Total 100
5. Core Concepts Deep Dive
Null Safety Architecture
┌─────────────────────────────────────────────────────────────┐
│ KOTLIN NULL SAFETY SYSTEM │
└─────────────────────────────────────────────────────────────┘
Type System:
────────────
Non-Nullable Type (String)
↓
Cannot be null
↓
Compile-time safety
Nullable Type (String?)
↓
Can be null
↓
Requires null checks
Operators:
──────────
?. Safe Call
Returns null if receiver is null
?: Elvis Operator
Provides default value
!! Not-null Assertion
Throws NPE if null (avoid!)
Example Flow:
─────────────
val name: String? = getName()
↓
Is it null?
↙ ↘
Yes No
↓ ↓
Use ?: Use value
default directly
Coroutines Architecture
┌─────────────────────────────────────────────────────────────┐
│ COROUTINES EXECUTION MODEL │
└─────────────────────────────────────────────────────────────┘
Thread Pool (Limited threads)
↓
┌─────────────────────────────────────┐
│ Dispatcher │
│ • Main (UI thread) │
│ • IO (Network/Disk) │
│ • Default (CPU-intensive) │
│ • Unconfined │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Coroutine Scope │
│ • GlobalScope │
│ • CoroutineScope │
│ • lifecycleScope (Android) │
│ • viewModelScope (Android) │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Coroutine Builders │
│ • launch (fire and forget) │
│ • async (returns Deferred) │
│ • runBlocking (blocks thread) │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Suspend Functions │
│ • Can pause execution │
│ • Don't block thread │
│ • Resume when ready │
└─────────────────────────────────────┘
Benefits:
─────────
• Lightweight (1000s of coroutines)
• Structured concurrency
• Exception handling
• Cancellation support
6. Memory and Performance
Memory Layout
┌─────────────────────────────────────────────────────────────┐
│ KOTLIN OBJECT MEMORY │
└─────────────────────────────────────────────────────────────┘
Data Class:
───────────
data class User(val id: Int, val name: String)
Memory:
┌──────────────────────────────────────┐
│ Object Header (12 bytes) │
├──────────────────────────────────────┤
│ id: Int (4 bytes) │
├──────────────────────────────────────┤
│ name: String reference (4 bytes) │
├──────────────────────────────────────┤
│ Padding (4 bytes) │
└──────────────────────────────────────┘
Total: 24 bytes + String object
Inline Class (Value Class):
───────────────────────────
@JvmInline
value class UserId(val value: Int)
Memory:
┌──────────────────────────────────────┐
│ No object wrapper! │
│ Just the Int value (4 bytes) │
└──────────────────────────────────────┘
Total: 4 bytes (no overhead!)
Sealed Class Hierarchy:
───────────────────────
sealed class Result
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
Memory efficient:
• No vtable overhead
• Compiler optimizations
• Exhaustive when expressions
7. Architecture Patterns
MVVM with Kotlin
┌─────────────────────────────────────────────────────────────┐
│ MVVM ARCHITECTURE │
└─────────────────────────────────────────────────────────────┘
View (Activity/Fragment)
↓ observes
ViewModel (StateFlow/LiveData)
↓ calls
Repository (Data abstraction)
↓ uses
Data Sources (API, Database, Cache)
Code Example:
─────────────
// ViewModel
class UserViewModel(
private val repository: UserRepository
) : ViewModel() {
private val _userState = MutableStateFlow<UiState>(UiState.Loading)
val userState: StateFlow<UiState> = _userState.asStateFlow()
fun loadUser(id: Long) {
viewModelScope.launch {
_userState.value = UiState.Loading
try {
val user = repository.getUser(id)
_userState.value = UiState.Success(user)
} catch (e: Exception) {
_userState.value = UiState.Error(e.message)
}
}
}
}
// Repository
class UserRepository(
private val api: UserApi,
private val dao: UserDao
) {
suspend fun getUser(id: Long): User {
return try {
// Try network first
api.getUser(id).also { user ->
dao.insert(user)
}
} catch (e: Exception) {
// Fallback to cache
dao.getUser(id) ?: throw e
}
}
}
8. Interview Questions with Answers
Beginner Level (1-25)
Q1: What is Kotlin and why was it created?
Answer: Kotlin is a modern, statically-typed programming language developed by JetBrains in 2011. It was created to address Java's limitations while maintaining 100% interoperability.
Key reasons:
- Reduce boilerplate code
- Improve null safety
- Add modern language features
- Maintain Java compatibility
- Increase developer productivity
// Java (verbose)
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
// equals, hashCode, toString...
}
// Kotlin (concise)
data class User(val name: String, val age: Int)
Q2: Explain val vs var
Answer:
val(value) = immutable reference (like Java'sfinal)var(variable) = mutable reference
val name = "John" // Cannot reassign
// name = "Jane" // Compile error!
var age = 30 // Can reassign
age = 31 // OK
// Important: val doesn't mean the object is immutable
val list = mutableListOf(1, 2, 3)
list.add(4) // OK! The reference is immutable, not the object
Best Practice: Prefer val over var for immutability.
Q3: What is null safety in Kotlin?
Answer: Kotlin's type system distinguishes between nullable and non-nullable types, eliminating most NullPointerExceptions at compile time.
// Non-nullable (default)
var name: String = "John"
// name = null // Compile error!
// Nullable (explicit)
var name: String? = "John"
name = null // OK
// Safe call operator
val length = name?.length // Returns null if name is null
// Elvis operator (default value)
val length = name?.length ?: 0 // Returns 0 if name is null
// Not-null assertion (avoid!)
val length = name!!.length // Throws NPE if name is null
Q4: Explain data classes
Answer: Data classes are classes designed to hold data. Kotlin automatically generates:
equals()andhashCode()toString()copy()functioncomponentN()functions for destructuring
data class User(
val id: Long,
val name: String,
val email: String
)
// Usage
val user = User(1, "John", "[email protected]")
// Auto-generated toString()
println(user) // User(id=1, name=John, [email protected])
// Auto-generated copy()
val updatedUser = user.copy(name = "Jane")
// Destructuring
val (id, name, email) = user
Q5: What are extension functions?
Answer: Extension functions allow you to add new functions to existing classes without modifying their source code or using inheritance.
// Define extension function
fun String.isPalindrome(): Boolean {
return this == this.reversed()
}
// Usage
"radar".isPalindrome() // true
"hello".isPalindrome() // false
// Real-world example
fun Int.toFormattedString(): String {
return NumberFormat.getInstance().format(this)
}
1000000.toFormattedString() // "1,000,000"
Intermediate Level (26-60)
Q26: Explain sealed classes
Answer: Sealed classes restrict class hierarchies, allowing you to represent restricted class hierarchies where a value can have one of the types from a limited set.
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
object Loading : Result<Nothing>()
}
// Usage with exhaustive when
fun handleResult(result: Result<User>) {
when (result) {
is Result.Success -> println("User: ${result.data}")
is Result.Error -> println("Error: ${result.exception}")
is Result.Loading -> println("Loading...")
// No else needed - compiler knows all cases
}
}
Q27: What is the difference between object and class?
Answer:
| Feature | class | object |
|---|---|---|
| Instances | Multiple | Single (Singleton) |
| Constructor | Yes | No |
| Inheritance | Yes | Yes |
| Use case | Regular objects | Singleton, companion |
// Class - multiple instances
class User(val name: String)
val user1 = User("John")
val user2 = User("Jane")
// Object - singleton
object Database {
fun connect() {
println("Connected")
}
}
Database.connect() // Only one instance
// Companion object (like Java static)
class User {
companion object {
const val MAX_AGE = 150
fun create(name: String) = User(name)
}
}
User.MAX_AGE
User.create("John")
Q28: Explain higher-order functions
Answer: Functions that take functions as parameters or return functions.
// Function that takes a function as parameter
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}
// Usage
val sum = calculate(5, 3) { x, y -> x + y } // 8
val product = calculate(5, 3) { x, y -> x * y } // 15
// Function that returns a function
fun makeMultiplier(factor: Int): (Int) -> Int {
return { number -> number * factor }
}
val double = makeMultiplier(2)
double(5) // 10
Q29: What is lateinit and when to use it?
Answer: lateinit allows you to declare a non-nullable property without initializing it immediately. Used for dependency injection and Android views.
class MyActivity : AppCompatActivity() {
// Without lateinit - must initialize
private var viewModel: MyViewModel? = null
// With lateinit - cleaner
private lateinit var viewModel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
}
// Check if initialized
fun doSomething() {
if (::viewModel.isInitialized) {
viewModel.loadData()
}
}
}
// Restrictions:
// • Only for var (not val)
// • Only for non-nullable types
// • Cannot be primitive types
// • Must be initialized before use
Q30: Explain lazy initialization
Answer: lazy is a delegate that initializes a property only when it's first accessed. Thread-safe by default.
class ExpensiveObject {
init {
println("Creating expensive object...")
}
}
class MyClass {
// Initialized only when first accessed
val expensive: ExpensiveObject by lazy {
println("Initializing...")
ExpensiveObject()
}
}
val obj = MyClass()
println("Object created")
// Output: Object created
println(obj.expensive)
// Output:
// Initializing...
// Creating expensive object...
println(obj.expensive)
// Output: (nothing - already initialized)
// Lazy modes
val lazyValue: String by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
// Thread-safe (default)
"value"
}
val lazyValue2: String by lazy(LazyThreadSafetyMode.NONE) {
// Not thread-safe (faster)
"value"
}
Advanced Level (61-100)
Q61: Explain coroutines vs threads
Answer:
| Feature | Thread | Coroutine |
|---|---|---|
| Weight | Heavy (~1MB) | Lightweight (~KB) |
| Creation cost | Expensive | Cheap |
| Context switching | Expensive | Cheap |
| Scalability | Limited (1000s) | High (millions) |
| Blocking | Blocks thread | Suspends, doesn't block |
// Threads - limited scalability
repeat(100_000) {
thread {
Thread.sleep(1000)
println(".")
}
}
// OutOfMemoryError!
// Coroutines - highly scalable
runBlocking {
repeat(100_000) {
launch {
delay(1000)
print(".")
}
}
}
// Works fine!
Q62: What is a suspend function?
Answer: A function that can be paused and resumed without blocking the thread. Can only be called from coroutines or other suspend functions.
suspend fun fetchUser(id: Long): User {
delay(1000) // Suspends, doesn't block
return api.getUser(id)
}
suspend fun fetchUserWithPosts(id: Long): UserWithPosts {
// Sequential execution
val user = fetchUser(id)
val posts = fetchPosts(id)
return UserWithPosts(user, posts)
}
suspend fun fetchUserWithPostsParallel(id: Long): UserWithPosts {
// Parallel execution
coroutineScope {
val userDeferred = async { fetchUser(id) }
val postsDeferred = async { fetchPosts(id) }
return UserWithPosts(
userDeferred.await(),
postsDeferred.await()
)
}
}
Q63: Explain Flow in Kotlin
Answer: Flow is a cold asynchronous stream that emits values sequentially. Like RxJava's Observable but built on coroutines.
// Create Flow
fun getNumbers(): Flow<Int> = flow {
for (i in 1..5) {
delay(100)
emit(i)
}
}
// Collect Flow
lifecycleScope.launch {
getNumbers()
.map { it * 2 }
.filter { it > 5 }
.collect { value ->
println(value)
}
}
// StateFlow (hot stream, always has value)
class ViewModel : ViewModel() {
private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
fun loadData() {
viewModelScope.launch {
_uiState.value = UiState.Loading
try {
val data = repository.getData()
_uiState.value = UiState.Success(data)
} catch (e: Exception) {
_uiState.value = UiState.Error(e.message)
}
}
}
}
// SharedFlow (hot stream, no initial value)
private val _events = MutableSharedFlow<Event>()
val events: SharedFlow<Event> = _events.asSharedFlow()
Q64: What is inline function and when to use it?
Answer: inline functions copy the function body to the call site, eliminating function call overhead. Useful for higher-order functions.
// Without inline - function call overhead
fun <T> lock(lock: Lock, body: () -> T): T {
lock.lock()
try {
return body()
} finally {
lock.unlock()
}
}
// With inline - no overhead
inline fun <T> lock(lock: Lock, body: () -> T): T {
lock.lock()
try {
return body()
} finally {
lock.unlock()
}
}
// Usage
lock(myLock) {
// This code is inlined at call site
doSomething()
}
// Becomes (after compilation):
myLock.lock()
try {
doSomething()
} finally {
myLock.unlock()
}
// noinline - prevent specific lambda from being inlined
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {
// ...
}
// crossinline - lambda cannot have non-local returns
inline fun foo(crossinline body: () -> Unit) {
// ...
}
Q65: Explain reified type parameters
Answer: reified allows you to access the type parameter at runtime in inline functions.
// Without reified - type erasure
fun <T> getType(): Class<T> {
// return T::class.java // Error: Cannot use T as reified type
}
// With reified - type available at runtime
inline fun <reified T> getType(): Class<T> {
return T::class.java // Works!
}
// Real-world example
inline fun <reified T> Gson.fromJson(json: String): T {
return fromJson(json, T::class.java)
}
// Usage
val user: User = gson.fromJson(jsonString) // Type inferred
// Android example
inline fun <reified T : Activity> Context.startActivity() {
startActivity(Intent(this, T::class.java))
}
// Usage
startActivity<MainActivity>()
9. Common Mistakes
Mistake 1: Using !! operator
// ❌ Wrong: Defeats null safety
val name = user!!.name // Can throw NPE
// ✅ Correct: Use safe call
val name = user?.name
// ✅ Better: Use Elvis operator
val name = user?.name ?: "Unknown"
// ✅ Best: Handle null explicitly
val name = if (user != null) {
user.name
} else {
"Unknown"
}
Mistake 2: Not using data classes
// ❌ Wrong: Regular class for data
class User(val name: String, val age: Int) {
override fun equals(other: Any?): Boolean {
// Manual implementation
}
override fun hashCode(): Int {
// Manual implementation
}
override fun toString(): String {
// Manual implementation
}
}
// ✅ Correct: Data class
data class User(val name: String, val age: Int)
// Auto-generates equals, hashCode, toString, copy, componentN
Mistake 3: Blocking in coroutines
// ❌ Wrong: Blocking call in coroutine
lifecycleScope.launch {
Thread.sleep(1000) // Blocks thread!
updateUI()
}
// ✅ Correct: Suspend function
lifecycleScope.launch {
delay(1000) // Suspends, doesn't block
updateUI()
}
Mistake 4: Not using scope functions
// ❌ Wrong: Repetitive code
val user = User()
user.name = "John"
user.age = 30
user.email = "[email protected]"
saveUser(user)
// ✅ Correct: Use apply
val user = User().apply {
name = "John"
age = 30
email = "[email protected]"
}
saveUser(user)
// ✅ Alternative: Use also
val user = User().also {
it.name = "John"
it.age = 30
it.email = "[email protected]"
}
Mistake 5: Mutable collections as return types
// ❌ Wrong: Exposes mutable collection
class UserRepository {
private val users = mutableListOf<User>()
fun getUsers(): MutableList<User> = users // Can be modified!
}
// ✅ Correct: Return immutable view
class UserRepository {
private val users = mutableListOf<User>()
fun getUsers(): List<User> = users.toList() // Defensive copy
}
// ✅ Better: Use immutable collections
class UserRepository {
private val _users = mutableListOf<User>()
val users: List<User> get() = _users
}
10. Performance Considerations
1. Use Sequences for Large Collections
// ❌ Slow: Creates intermediate collections
val result = (1..1_000_000)
.map { it * 2 } // Creates list of 1M elements
.filter { it > 100 } // Creates another list
.take(10) // Creates final list
// ✅ Fast: Lazy evaluation
val result = (1..1_000_000)
.asSequence()
.map { it * 2 } // Lazy
.filter { it > 100 } // Lazy
.take(10) // Only processes 10 elements
.toList()
2. Use Inline Classes for Type Safety
// ❌ Memory overhead
data class UserId(val value: Long) // 24 bytes
// ✅ No overhead
@JvmInline
value class UserId(val value: Long) // 8 bytes (just the Long)
// Usage
fun getUser(id: UserId): User {
// Type-safe, no runtime overhead
}
3. Avoid Unnecessary Object Creation
// ❌ Creates new list every time
fun getUsers(): List<User> {
return listOf(user1, user2, user3)
}
// ✅ Reuse immutable list
private val users = listOf(user1, user2, user3)
fun getUsers(): List<User> = users
4. Use Appropriate Collection Types
// ❌ Wrong: List for lookups
val users = listOf(user1, user2, user3)
users.find { it.id == targetId } // O(n)
// ✅ Correct: Map for lookups
val users = mapOf(
user1.id to user1,
user2.id to user2,
user3.id to user3
)
users[targetId] // O(1)
11. Advanced Interview Questions
Q91: How does Kotlin handle SAM conversions?
Answer: SAM (Single Abstract Method) conversion allows lambda expressions to be used where functional interfaces are expected.
// Java interface
interface OnClickListener {
fun onClick(view: View)
}
// ❌ Java way
button.setOnClickListener(object : OnClickListener {
override fun onClick(view: View) {
// Handle click
}
})
// ✅ Kotlin SAM conversion
button.setOnClickListener { view ->
// Handle click
}
// Even simpler
button.setOnClickListener {
// Handle click (parameter not used)
}
Q92: Explain delegation in Kotlin
Answer: Delegation allows an object to delegate some of its responsibilities to another object.
// Interface delegation
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { println(x) }
}
// Delegate to BaseImpl
class Derived(b: Base) : Base by b
// Usage
val base = BaseImpl(10)
val derived = Derived(base)
derived.print() // Delegates to base.print()
// Property delegation
class Example {
var p: String by Delegate()
}
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, delegated '${property.name}'"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value assigned to '${property.name}'")
}
}
Q93: What is the difference between map and flatMap?
Answer:
map: Transforms each element (1-to-1)flatMap: Transforms and flattens (1-to-many)
val numbers = listOf(1, 2, 3)
// map: List<Int> -> List<List<Int>>
val mapped = numbers.map { listOf(it, it * 2) }
// [[1, 2], [2, 4], [3, 6]]
// flatMap: List<Int> -> List<Int>
val flatMapped = numbers.flatMap { listOf(it, it * 2) }
// [1, 2, 2, 4, 3, 6]
// Real-world example
data class User(val id: Int, val friends: List<Int>)
val users = listOf(
User(1, listOf(2, 3)),
User(2, listOf(1, 4)),
User(3, listOf(1))
)
// Get all friend IDs
val allFriends = users.flatMap { it.friends }
// [2, 3, 1, 4, 1]
Q94: Explain context receivers (Kotlin 1.6.20+)
Answer: Context receivers allow functions to require specific contexts without explicit parameters.
// Without context receivers
fun logMessage(logger: Logger, message: String) {
logger.log(message)
}
// With context receivers
context(Logger)
fun logMessage(message: String) {
log(message) // Logger available in context
}
// Usage
with(myLogger) {
logMessage("Hello") // Logger is implicit
}
// Multiple contexts
context(Logger, Database)
fun saveAndLog(user: User) {
save(user) // Database context
log("Saved") // Logger context
}
Q95: How do you handle backpressure in Flow?
Answer: Use operators like buffer, conflate, or collectLatest.
// Problem: Slow collector
flow {
repeat(100) {
emit(it)
delay(100) // Fast producer
}
}.collect {
delay(1000) // Slow consumer
println(it)
}
// Solution 1: Buffer
flow {
repeat(100) {
emit(it)
delay(100)
}
}.buffer(50) // Buffer up to 50 items
.collect {
delay(1000)
println(it)
}
// Solution 2: Conflate (keep only latest)
flow {
repeat(100) {
emit(it)
delay(100)
}
}.conflate() // Drop intermediate values
.collect {
delay(1000)
println(it)
}
// Solution 3: collectLatest (cancel previous)
flow {
repeat(100) {
emit(it)
delay(100)
}
}.collectLatest {
delay(1000) // Cancelled if new value arrives
println(it)
}
12. Cheat Sheet
Quick Reference
// ═══════════════════════════════════════════════════════════
// BASICS
// ═══════════════════════════════════════════════════════════
val immutable = "value" // Immutable reference
var mutable = "value" // Mutable reference
const val CONSTANT = "value" // Compile-time constant
// ═══════════════════════════════════════════════════════════
// NULL SAFETY
// ═══════════════════════════════════════════════════════════
var nullable: String? = null // Nullable type
val length = str?.length // Safe call
val length = str?.length ?: 0 // Elvis operator
val length = str!!.length // Not-null assertion (avoid!)
// ═══════════════════════════════════════════════════════════
// FUNCTIONS
// ═══════════════════════════════════════════════════════════
fun add(a: Int, b: Int): Int = a + b // Single expression
fun greet(name: String = "Guest") {} // Default parameter
fun varargs(vararg items: String) {} // Variable arguments
// ═══════════════════════════════════════════════════════════
// CLASSES
// ═══════════════════════════════════════════════════════════
data class User(val name: String) // Data class
object Singleton {} // Singleton
sealed class Result {} // Sealed class
enum class Status { ACTIVE, INACTIVE } // Enum
// ═══════════════════════════════════════════════════════════
// COLLECTIONS
// ═══════════════════════════════════════════════════════════
val list = listOf(1, 2, 3) // Immutable list
val mutableList = mutableListOf(1) // Mutable list
val map = mapOf("key" to "value") // Immutable map
val set = setOf(1, 2, 3) // Immutable set
// ═══════════════════════════════════════════════════════════
// FUNCTIONAL
// ═══════════════════════════════════════════════════════════
list.map { it * 2 } // Transform
list.filter { it > 5 } // Filter
list.reduce { acc, i -> acc + i } // Reduce
list.forEach { println(it) } // Iterate
// ═══════════════════════════════════════════════════════════
// COROUTINES
// ═══════════════════════════════════════════════════════════
launch { } // Fire and forget
async { }.await() // Return value
suspend fun fetch() {} // Suspend function
delay(1000) // Non-blocking delay
// ═══════════════════════════════════════════════════════════
// SCOPE FUNCTIONS
// ═══════════════════════════════════════════════════════════
obj.let { } // Transform, returns lambda result
obj.apply { } // Configure, returns obj
obj.also { } // Side effects, returns obj
obj.run { } // Execute, returns lambda result
with(obj) { } // Execute, returns lambda result
Common Patterns
// Singleton
object Database {
fun connect() {}
}
// Factory
class User private constructor(val name: String) {
companion object {
fun create(name: String) = User(name)
}
}
// Builder
class User private constructor(
val name: String,
val age: Int,
val email: String?
) {
class Builder {
private var name: String = ""
private var age: Int = 0
private var email: String? = null
fun name(name: String) = apply { this.name = name }
fun age(age: Int) = apply { this.age = age }
fun email(email: String) = apply { this.email = email }
fun build() = User(name, age, email)
}
}
// Sealed class for state
sealed class UiState {
object Loading : UiState()
data class Success(val data: String) : UiState()
data class Error(val message: String) : UiState()
}
13. Summary
Key Takeaways
✅ Null Safety - Eliminates NPE at compile time
✅ Concise Syntax - Less boilerplate than Java
✅ Coroutines - Lightweight async programming
✅ Extension Functions - Add functionality without inheritance
✅ Data Classes - Auto-generated methods
✅ Sealed Classes - Restricted hierarchies
✅ Smart Casts - Automatic type casting
✅ Functional Programming - Higher-order functions, lambdas
✅ Java Interoperability - 100% compatible
✅ Modern Features - Inline classes, context receivers
Interview Preparation Tips
- Practice coding - Write Kotlin code daily
- Understand internals - Know how features work under the hood
- Real-world examples - Relate concepts to actual projects
- Compare with Java - Understand the differences
- Android specifics - If targeting Android roles
- Coroutines mastery - Critical for modern Kotlin
- Performance - Know optimization techniques
- Best practices - Follow Kotlin idioms
Most Asked Topics
Priority Topic Frequency
────────────────────────────────────────────
⭐⭐⭐ Null Safety 95%
⭐⭐⭐ Coroutines 90%
⭐⭐⭐ Data Classes 85%
⭐⭐ Extension Functions 80%
⭐⭐ Sealed Classes 75%
⭐⭐ Collections 70%
⭐⭐ Higher-Order Functions 65%
⭐ Delegation 50%
⭐ Inline Functions 45%
⭐ Reified Types 40%
Next Steps
- Practice on Kotlin Koans
- Build projects with Kotlin
- Contribute to open source
- Read Kotlin documentation
- Follow Kotlin blog