LearnThatStack Ace your next interview

Kotlin Mobile Development.
Interview cheat sheet.

Quick reference for Kotlin Mobile Development - sectioned for fast scanning. Skim the part you're shaky on, walk in confident.

Mobile Development 15-section reference ~11 min read

Overview

Kotlin is a modern, statically-typed programming language that runs on the JVM, Android, JavaScript, and Native platforms. Developed by JetBrains and officially supported by Google for Android development since 2017, Kotlin offers concise syntax, null safety, and seamless Java interoperability. It has become the preferred language for Android development, with over 95% of top Android apps using Kotlin.

1. Kotlin Basics

Variables and Data Types

// Immutable (read-only)
val name: String = "John"
val age = 25 // Type inference

// Mutable
var count: Int = 0
var price = 19.99 // Double

// Basic types
val byte: Byte = 127
val short: Short = 32767
val int: Int = 2147483647
val long: Long = 9223372036854775807L
val float: Float = 3.14f
val double: Double = 3.14159
val char: Char = 'A'
val boolean: Boolean = true

Null Safety

// Nullable types
var nullable: String? = null
val length = nullable?.length // Safe call
val lengthOrZero = nullable?.length ?: 0 // Elvis operator

// Non-null assertion (use carefully!)
val length2 = nullable!!.length // Throws NPE if null

// Safe cast
val str: String? = obj as? String

String Templates

val name = "Alice"
val greeting = "Hello, $name"
val expression = "2 + 2 = ${2 + 2}"

// Multiline strings
val text = """
    |First line
    |Second line
""".trimMargin()

2. Functions

Basic Functions

// Function with return type
fun add(a: Int, b: Int): Int {
    return a + b
}

// Single expression function
fun multiply(a: Int, b: Int) = a * b

// Default parameters
fun greet(name: String = "Guest") = "Hello, $name"

// Named arguments
greet(name = "John")

Extension Functions

// Extend existing classes
fun String.isPalindrome(): Boolean {
    return this == this.reversed()
}

"radar".isPalindrome() // true

// Extension properties
val String.lastChar: Char
    get() = this[length - 1]

Higher-Order Functions

// Function as parameter
fun calculate(x: Int, y: Int, operation: (Int, Int) -> Int): Int {
    return operation(x, y)
}

calculate(5, 3, { a, b -> a + b }) // 8
calculate(5, 3) { a, b -> a * b } // 15 (trailing lambda)

// Function returning function
fun getOperation(op: String): (Int, Int) -> Int {
    return when(op) {
        "+" -> { a, b -> a + b }
        "*" -> { a, b -> a * b }
        else -> { _, _ -> 0 }
    }
}

3. Classes and Objects

Classes

// Primary constructor
class Person(val name: String, var age: Int) {
    // Secondary constructor
    constructor(name: String) : this(name, 0)
    
    // Init block
    init {
        println("Person created: $name")
    }
}

// Data class (equals, hashCode, toString, copy)
data class User(val id: Int, val name: String)

// Sealed class (restricted hierarchy)
sealed class Result {
    data class Success(val data: String) : Result()
    data class Error(val message: String) : Result()
}

Inheritance

open class Animal(val name: String) {
    open fun sound() = "Some sound"
}

class Dog(name: String) : Animal(name) {
    override fun sound() = "Woof!"
}

// Interfaces
interface Clickable {
    fun click()
    fun showOff() = println("I'm clickable!") // Default implementation
}

class Button : Clickable {
    override fun click() = println("Button clicked")
}

Object and Companion Object

// Singleton
object Database {
    fun connect() { /* ... */ }
}

class MyClass {
    companion object {
        const val CONSTANT = "VALUE"
        fun factory(): MyClass = MyClass()
    }
}

4. Collections

List, Set, Map

// Immutable collections
val list = listOf(1, 2, 3)
val set = setOf("a", "b", "c")
val map = mapOf("key1" to "value1", "key2" to "value2")

// Mutable collections
val mutableList = mutableListOf(1, 2, 3)
mutableList.add(4)

val mutableMap = mutableMapOf<String, Int>()
mutableMap["key"] = 10

Collection Operations

val numbers = listOf(1, 2, 3, 4, 5)

// Transformations
numbers.map { it * 2 } // [2, 4, 6, 8, 10]
numbers.filter { it % 2 == 0 } // [2, 4]
numbers.flatMap { listOf(it, it * 2) } // [1, 2, 2, 4, 3, 6, 4, 8, 5, 10]

// Aggregations
numbers.sum() // 15
numbers.reduce { acc, n -> acc + n } // 15
numbers.fold(0) { acc, n -> acc + n } // 15

// Grouping
val words = listOf("apple", "banana", "apricot", "berry")
words.groupBy { it.first() } // {a=[apple, apricot], b=[banana, berry]}

// Sequences (lazy evaluation)
numbers.asSequence()
    .filter { it % 2 == 0 }
    .map { it * 2 }
    .toList()

5. Coroutines

Basics

// Launch coroutine
GlobalScope.launch {
    delay(1000) // Non-blocking delay
    println("World!")
}
println("Hello,")

// Async/await
val deferred = async {
    delay(1000)
    "Result"
}
val result = deferred.await()

// RunBlocking (for testing/main)
runBlocking {
    delay(1000)
    println("Done")
}

Coroutine Scope

class MyActivity : CoroutineScope {
    private val job = Job()
    override val coroutineContext = Dispatchers.Main + job
    
    fun loadData() {
        launch {
            val data = withContext(Dispatchers.IO) {
                // Network/DB operation
                fetchData()
            }
            updateUI(data)
        }
    }
    
    fun onDestroy() {
        job.cancel() // Cancel all coroutines
    }
}

Flow

// Create flow
fun numbersFlow(): Flow<Int> = flow {
    for (i in 1..5) {
        delay(100)
        emit(i)
    }
}

// Collect flow
numbersFlow()
    .map { it * 2 }
    .filter { it > 5 }
    .collect { println(it) }

// StateFlow (hot flow)
val stateFlow = MutableStateFlow(0)
stateFlow.value = 1

6. Android Specific

Activity/Fragment

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // View binding
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        binding.button.setOnClickListener {
            // Handle click
        }
    }
}

ViewModel

class MyViewModel : ViewModel() {
    private val _uiState = MutableLiveData<String>()
    val uiState: LiveData<String> = _uiState
    
    fun updateData() {
        viewModelScope.launch {
            val result = repository.fetchData()
            _uiState.value = result
        }
    }
}

// In Activity/Fragment
viewModel.uiState.observe(this) { data ->
    // Update UI
}

Jetpack Compose Basics

@Composable
fun Greeting(name: String) {
    Text(text = "Hello, $name!")
}

@Composable
fun MyScreen() {
    var count by remember { mutableStateOf(0) }
    
    Column {
        Text("Count: $count")
        Button(onClick = { count++ }) {
            Text("Increment")
        }
    }
}

7. Delegation

Property Delegation

// Lazy initialization
val expensive: String by lazy {
    println("Computing...")
    "Result"
}

// Observable properties
var name: String by Delegates.observable("initial") { _, old, new ->
    println("$old -> $new")
}

// Custom delegate
class Example {
    var p: String by Delegate()
}

class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "Delegated value"
    }
    
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("Setting $value")
    }
}

8. Generics

Generic Classes and Functions

// Generic class
class Box<T>(val content: T)

// Generic function
fun <T> singletonList(item: T): List<T> = listOf(item)

// Constraints
fun <T : Comparable<T>> max(a: T, b: T): T {
    return if (a > b) a else b
}

// Multiple constraints
fun <T> process(item: T) where T : CharSequence, T : Comparable<T> {
    // ...
}

Variance

// Covariance (out) - Producer
interface Producer<out T> {
    fun produce(): T
}

// Contravariance (in) - Consumer
interface Consumer<in T> {
    fun consume(item: T)
}

// Invariance (default)
class Box<T>(var value: T)

// Star projection
fun printAll(items: List<*>) {
    items.forEach { println(it) }
}

9. Scope Functions

val person = Person("Alice", 25)

// let - it
person.let {
    println("${it.name} is ${it.age} years old")
}

// run - this
val result = person.run {
    age += 1
    "Happy ${age}th birthday, $name!"
}

// with - this
with(person) {
    println("$name is $age years old")
}

// apply - this (returns object)
val configured = person.apply {
    age = 30
    name = "Alice Smith"
}

// also - it (returns object)
person.also {
    println("Processing ${it.name}")
}

10. Common Interview Patterns

Singleton Pattern

// Object declaration
object Singleton {
    fun doSomething() { }
}

// Class with companion object
class Singleton private constructor() {
    companion object {
        @JvmStatic
        val instance = Singleton()
    }
}

Builder Pattern

class Pizza private constructor(
    val size: String,
    val cheese: Boolean,
    val pepperoni: Boolean
) {
    class Builder {
        private var size = "medium"
        private var cheese = true
        private var pepperoni = false
        
        fun size(size: String) = apply { this.size = size }
        fun cheese(value: Boolean) = apply { cheese = value }
        fun pepperoni(value: Boolean) = apply { pepperoni = value }
        fun build() = Pizza(size, cheese, pepperoni)
    }
}

val pizza = Pizza.Builder()
    .size("large")
    .pepperoni(true)
    .build()

Factory Pattern

interface Animal {
    fun speak()
}

class Dog : Animal {
    override fun speak() = println("Woof!")
}

class Cat : Animal {
    override fun speak() = println("Meow!")
}

object AnimalFactory {
    fun createAnimal(type: String): Animal = when(type) {
        "dog" -> Dog()
        "cat" -> Cat()
        else -> throw IllegalArgumentException("Unknown animal")
    }
}

11. Best Practices & Tips

Idiomatic Kotlin

// Use when for multiple conditions
fun describe(x: Any) = when(x) {
    is String -> "String of length ${x.length}"
    is Int -> "Integer: $x"
    else -> "Unknown"
}

// Prefer immutability
val list = mutableListOf<Int>().apply {
    add(1)
    add(2)
}.toList() // Convert to immutable

// Use inline functions for performance
inline fun measureTime(block: () -> Unit) {
    val start = System.currentTimeMillis()
    block()
    println("Took ${System.currentTimeMillis() - start}ms")
}

Critical Interview Topics Explained

1. Null Safety - Kotlin's Killer Feature

// Types are non-null by default
var name: String = "John"  // Cannot be null
name = null  // Compilation error!

// Nullable types require ?
var nullableName: String? = "John"
nullableName = null  // OK

// Safe call operator ?.
val length = nullableName?.length  // Returns Int? (null if nullableName is null)

// Elvis operator ?: provides default
val len = nullableName?.length ?: 0  // Returns 0 if null

// Non-null assertion !! (use sparingly!)
val len2 = nullableName!!.length  // Throws NPE if null

// Smart casting
if (nullableName != null) {
    // nullableName is automatically cast to non-null String here
    println(nullableName.length)
}

2. val vs var - Immutability Matters

Aspect val var
Mutability Read-only reference Mutable reference
Reassignment Cannot reassign Can reassign
Object Mutability Object itself can be mutable Object itself can be mutable
Thread Safety Reference is thread-safe Not thread-safe
Best Practice Prefer val Use when needed
val list1 = mutableListOf(1, 2)  // Reference immutable
list1.add(3)  // OK - object is mutable
list1 = mutableListOf(4, 5)  // Error - cannot reassign

var list2 = mutableListOf(1, 2)  // Reference mutable
list2.add(3)  // OK
list2 = mutableListOf(4, 5)  // OK - can reassign

3. Coroutines vs Threads - Modern Concurrency

Aspect Coroutines Threads
Weight Lightweight (1000s possible) Heavy (limited by OS)
Memory ~2KB per coroutine ~1MB per thread
Creation Cost Cheap Expensive
Scheduling Cooperative Preemptive
Blocking Suspends, doesn't block thread Blocks thread
Debugging Easier with structured concurrency Complex
Cancellation Built-in support Manual handling

4. Inline Functions - Performance Optimization

// Without inline: Creates function object (overhead)
fun regularHigherOrder(block: () -> Unit) {
    block()
}

// With inline: No function object, code inserted at call site
inline fun inlinedHigherOrder(block: () -> Unit) {
    block()  // Code is copied to call site
}

// Benefits:
// 1. No allocation overhead for lambda
// 2. Allows non-local returns
// 3. Can use reified type parameters

inline fun <reified T> isInstance(value: Any): Boolean {
    return value is T  // Can check type at runtime
}

5. Sealed Classes - Type-Safe State Management

// All subclasses must be defined in same file (before Kotlin 1.5)
// or same package (Kotlin 1.5+)
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>()
}

// Exhaustive when expression (no else needed)
fun handleResult(result: Result<String>) {
    when (result) {
        is Result.Success -> println(result.data)
        is Result.Error -> println(result.exception)
        Result.Loading -> println("Loading...")
        // Compiler knows all cases are covered
    }
}

6. Equality in Kotlin

data class Person(val name: String, val age: Int)

val p1 = Person("John", 25)
val p2 = Person("John", 25)
val p3 = p1

// Structural equality (calls equals())
p1 == p2  // true - same content
p1 == p3  // true

// Referential equality (same object in memory)
p1 === p2  // false - different objects
p1 === p3  // true - same object

Memory & Performance

// Avoid creating unnecessary objects
// Bad
list.filter { it > 0 }.map { it * 2 }

// Good (use sequence for large collections)
list.asSequence()
    .filter { it > 0 }
    .map { it * 2 }
    .toList()

// Use const for compile-time constants
companion object {
    const val MAX_COUNT = 100 // Compile-time
    val MIN_COUNT = 0 // Runtime
}

Performance & Best Practices

Collection Performance Tips

// Use sequences for large collections with multiple operations
val result = list
    .asSequence()  // Lazy evaluation
    .filter { it > 0 }
    .map { it * 2 }
    .take(5)
    .toList()  // Terminal operation

// Use appropriate collection types
val readOnlyList: List<Int> = listOf(1, 2, 3)  // Immutable
val mutableList: MutableList<Int> = mutableListOf(1, 2, 3)
val arrayList: ArrayList<Int> = arrayListOf(1, 2, 3)  // When you need ArrayList specifically

// For small maps with Int keys
val sparseArray = SparseArray<String>()  // More memory efficient than HashMap

Android-Specific Best Practices

  1. Use Parcelize for Parcelable:
@Parcelize
data class User(val name: String, val age: Int) : Parcelable
  1. Scope Functions for View Binding:
binding.apply {
    userName.text = user.name
    userAge.text = user.age.toString()
    submitButton.setOnClickListener { submit() }
}
  1. Extension Functions for Cleaner Code:
fun View.show() { visibility = View.VISIBLE }
fun View.hide() { visibility = View.GONE }
fun Context.toast(message: String) {
    Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}

Quick Reference

Essential Keywords

  • val/var: Immutable/mutable variables
  • fun: Function declaration
  • class/interface: Type declarations
  • object: Singleton/anonymous object
  • companion object: Static members
  • data class: Auto-generated methods
  • sealed class: Restricted hierarchy
  • open/final: Inheritance control
  • override: Method overriding
  • inline/crossinline/noinline: Inline function modifiers
  • reified: Type parameters in inline functions
  • suspend: Coroutine functions
  • by: Delegation

Operators

  • ?: Nullable type
  • ?.: Safe call
  • ?:: Elvis operator
  • !!: Non-null assertion
  • as/as?: Cast/safe cast
  • in/!in: Contains check
  • ..: Range
  • ::: Reference operator

Interview Success Strategy

Top 10 Must-Know Concepts

  1. Null Safety - Safe calls, Elvis operator, smart casting
  2. Coroutines - launch, async/await, scopes, cancellation
  3. Collections - List vs MutableList, map/filter/reduce, sequences
  4. Scope Functions - let, run, with, apply, also
  5. Extension Functions - How to create and when to use
  6. Higher-Order Functions - Functions as parameters, lambdas
  7. Sealed Classes - When expressions, state management
  8. Data Classes - equals, hashCode, copy, destructuring
  9. Delegation - by lazy, observable, class delegation
  10. Generics - Variance (in, out), reified parameters

Common Coding Challenges

  • Implement a thread-safe singleton
  • Write a custom delegation property
  • Create a DSL using lambdas with receivers
  • Implement a generic repository pattern
  • Build a coroutine-based retry mechanism
  • Design a sealed class hierarchy for API responses

Red Flags to Avoid

  • Overusing !! (non-null assertion)
  • Not handling null cases properly
  • Blocking main thread with runBlocking
  • Creating unnecessary companion objects
  • Using var when val would work
  • Ignoring scope function conventions
  • Not leveraging Kotlin's standard library

Pro Tip: In interviews, always explain your choice of Kotlin features. Show that you understand not just the syntax but the why behind using specific language features. Demonstrate how Kotlin's features solve real problems and improve code quality, safety, and maintainability.

Found this useful? Pass it on.
Pro · $10/mo

The sheet is free. Pro goes deeper.

Pro opens the full question library behind every sheet, every refresher and a monthly AI allowance. One subscription, all formats.

Full question library All refreshers Cancel anytime