InterviewPitch
Kotlin interview questions

Kotlin Interview Questions with Answers

Most Asked Kotlin Interview Questions for Software Engineer Roles

100+ QuestionsDetailed AnswersCode ExamplesUpdated for 2026

Introduction

Kotlin is a modern, statically typed programming language that runs on the JVM and is fully interoperable with Java. Developed by JetBrains, Kotlin is designed to be concise, safe, and pragmatic. It has become the preferred language for Android development, officially supported by Google, and is increasingly used for backend, web, and cross-platform development. This comprehensive guide presents 100+ carefully curated Kotlin interview questions and answers, covering everything from the fundamentals to advanced topics. You'll master variables, data types, functions, classes, null safety, data classes, sealed classes, extension functions, higher-order functions, coroutines, flows, channels, generics, delegation, DSL, reflection, and real-world Android and backend development scenarios. Whether you're preparing for an Android developer role, a backend position with Kotlin, or a full-stack job, this question bank will solidify your understanding and give you the confidence to ace your interview. Start practicing now and become a Kotlin expert.

Why Kotlin?

  • 100% interoperable with Java – leverage existing Java libraries and frameworks
  • Null safety – eliminates null pointer exceptions with compile-time checks
  • Concise and expressive syntax – reduces boilerplate code significantly
  • Built-in coroutines – simplifies asynchronous programming and concurrency
  • Officially supported for Android development by Google
  • Growing ecosystem with full support for backend development (Spring Boot, Ktor)
  • Used by companies like Google, Netflix, Uber, and many more

Most Asked Kotlin Interview Questions

Beginner
1. What is Kotlin?

Kotlin is a modern, statically-typed programming language that runs on the JVM. It is designed to be concise, safe, interoperable with Java, and pragmatic.

  • Statically-typed: Type checking at compile time
  • Concise: Less boilerplate than Java
  • Null safety: Built-in null safety features
  • Interoperable: 100% compatible with Java
  • Multi-platform: JVM, JS, Native
kotlin
// Hello World in Kotlin
fun main() {
    println("Hello, World!")
}
Beginner
2. How to declare variables in Kotlin?

Variables in Kotlin are declared using var (mutable) and val (immutable) keywords. Type inference is supported.

  • val: Read-only variable (immutable)
  • var: Mutable variable
  • Type inference: Types can be omitted
  • Null safety: Type? for nullable types
  • Lateinit: Late initialization for non-null types
kotlin
// Variables in Kotlin
var mutableVar: String = "Hello"  // Mutable variable
val immutableVal: String = "World" // Immutable variable (read-only)
var inferred = "Inferred Type"    // Type inference

// Nullable variables
var nullable: String? = null

println(mutableVar)
println(immutableVal)
println(inferred)
println(nullable)
Beginner
3. What are the data types in Kotlin?

Kotlin has a rich type system with both primitive and reference types. All types are objects in Kotlin.

  • Numbers: Int, Long, Float, Double, Short, Byte
  • Boolean: true/false
  • Char: Single character
  • String: Sequence of characters
  • Array: Array of elements
  • Collections: List, Set, Map
kotlin
// Data Types in Kotlin
// Numbers
val intNum: Int = 10
val longNum: Long = 100L
val floatNum: Float = 3.14f
val doubleNum: Double = 3.14159
val shortNum: Short = 100
val byteNum: Byte = 127

// Booleans
val isActive: Boolean = true

// Characters
val char: Char = 'A'

// Strings
val str: String = "Hello Kotlin"

// Arrays
val arr: Array<Int> = arrayOf(1, 2, 3, 4, 5)

// Type checking
println(intNum is Int) // true
println(str is String) // true
Beginner
4. How to define functions in Kotlin?

Functions in Kotlin are defined with the fun keyword. They support default parameters, named arguments, and higher-order functions.

  • Basic: fun name(parameters): ReturnType
  • Single-expression: fun add(a: Int, b: Int) = a + b
  • Default params: fun greet(name: String = "Guest")
  • Unit return: fun print(): Unit
  • Higher-order: Functions that take/return functions
kotlin
// Functions in Kotlin
// Basic function
fun add(a: Int, b: Int): Int {
    return a + b
}

// Single-expression function
fun subtract(a: Int, b: Int) = a - b

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

// Function with unit return (void)
fun printMessage(message: String): Unit {
    println(message)
}

// Higher-order function
fun operate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

// Lambda expression
val multiply = { a: Int, b: Int -> a * b }

// Inline function
inline fun inlineExample() {
    println("Inline function")
}

// Usage
println(add(5, 3))
println(subtract(10, 4))
println(greet("Alice"))
printMessage("Hello")
println(operate(6, 7, multiply))
Beginner
5. What are arrays in Kotlin?

Arrays are collections of elements with a fixed size. Kotlin provides both Array class and primitive type arrays.

  • Creation: arrayOf(), intArrayOf()
  • Access: arr[index]
  • Operations: size, map, filter
  • Primitive arrays: IntArray, DoubleArray
  • Lists vs Arrays: Lists are dynamic, arrays are fixed
kotlin
// Arrays in Kotlin
// Array creation
val numbers = arrayOf(1, 2, 3, 4, 5)
val strings = arrayOf("Apple", "Banana", "Orange")
val mixed = arrayOf(1, "Hello", 3.14)

// Primitive arrays
val intArray = intArrayOf(1, 2, 3)
val doubleArray = doubleArrayOf(1.0, 2.0, 3.0)

// Array operations
println(numbers.size)
println(numbers[2]) // Access element
numbers[2] = 10 // Modify element

// Iteration
for (num in numbers) {
    println(num)
}

// Array functions
val doubled = numbers.map { it * 2 }
val filtered = numbers.filter { it > 2 }
val sum = numbers.sum()

// List vs Array
val list = listOf(1, 2, 3) // Immutable list
val mutableList = mutableListOf(1, 2, 3) // Mutable list

println(doubled)
println(filtered)
println(sum)
Beginner
6. What are collections in Kotlin?

Kotlin collections include List, Set, and Map with both immutable and mutable variants.

  • List: Ordered collection with duplicates
  • Set: Unordered collection without duplicates
  • Map: Key-value pairs
  • Immutable: listOf(), setOf(), mapOf()
  • Mutable: mutableListOf(), mutableSetOf()
  • Operations: filter, map, reduce
kotlin
// Collections in Kotlin
// List
val immutableList = listOf(1, 2, 3, 4, 5)
val mutableList = mutableListOf(1, 2, 3)
mutableList.add(4)
mutableList.remove(2)

// Set
val immutableSet = setOf(1, 2, 3, 3) // [1, 2, 3]
val mutableSet = mutableSetOf(1, 2, 3)
mutableSet.add(4)

// Map
val immutableMap = mapOf("key1" to "value1", "key2" to "value2")
val mutableMap = mutableMapOf("key1" to "value1")
mutableMap["key2"] = "value2"
mutableMap.remove("key1")

// Collection operations
val numbers = listOf(1, 2, 3, 4, 5, 6)
val evens = numbers.filter { it % 2 == 0 }
val doubled = numbers.map { it * 2 }
val sum = numbers.reduce { acc, i -> acc + i }
val exists = numbers.any { it > 10 }
val allEven = numbers.all { it % 2 == 0 }

println(evens)
println(doubled)
println(sum)
Beginner
7. What are data classes in Kotlin?

Data classes are special classes that automatically provide toString(), equals(), hashCode(), and copy() methods.

  • Declaration: data class Person(val name: String)
  • Copy: person.copy(name = "NewName")
  • Destructuring: val (name, age) = person
  • Component functions: Automatically generated
  • Limitations: Cannot be abstract, open, sealed, or inner
kotlin
// Data Classes in Kotlin
// Data class automatically provides toString, equals, hashCode, copy
data class Person(
    val name: String,
    val age: Int,
    val city: String = "Unknown"
)

// Usage
val person1 = Person("Alice", 25, "NYC")
val person2 = person1.copy(age = 26) // Copy with modification
val (name, age, city) = person1 // Destructuring

println(person1) // Person(name=Alice, age=25, city=NYC)
println(person2)
println("Name: $name, Age: $age, City: $city")

// Data class with validation
data class User(val username: String, val email: String) {
    init {
        require(username.isNotEmpty()) { "Username cannot be empty" }
        require(email.contains("@")) { "Invalid email" }
    }
}
Beginner
8. What are sealed classes in Kotlin?

Sealed classes are used to represent restricted class hierarchies. They are similar to enums but with more flexibility.

  • Declaration: sealed class Result
  • Subclasses: Defined in the same file
  • When expressions: Exhaustive when used
  • Data classes: Can be combined with data classes
  • Sealed interfaces: Available in Kotlin 1.5+
kotlin
// Sealed Classes in Kotlin
sealed class Result {
    data class Success(val data: String) : Result()
    data class Error(val message: String) : Result()
    object Loading : Result()
}

// Usage
fun handleResult(result: Result) {
    when (result) {
        is Result.Success -> println("Success: ${result.data}")
        is Result.Error -> println("Error: ${result.message}")
        Result.Loading -> println("Loading...")
    }
}

// Sealed interface
sealed interface Shape {
    data class Circle(val radius: Double) : Shape
    data class Rectangle(val width: Double, val height: Double) : Shape
    object Point : Shape
}

fun area(shape: Shape): Double = when (shape) {
    is Shape.Circle -> Math.PI * shape.radius * shape.radius
    is Shape.Rectangle -> shape.width * shape.height
    Shape.Point -> 0.0
}

// Usage
handleResult(Result.Success("Data loaded"))
println(area(Shape.Circle(5.0)))
Beginner
9. What is null safety in Kotlin?

Kotlin's null safety system helps prevent NullPointerException by distinguishing between nullable and non-nullable types.

  • Non-nullable: String cannot be null
  • Nullable: String? can be null
  • Safe call: ?.let operator
  • Elvis operator: ?: for default values
  • Not-null assertion: !! (use carefully)
kotlin
// Null Safety in Kotlin
// Nullable types
var nullableString: String? = null
var nonNullableString: String = "Hello"

// Safe call operator
val length = nullableString?.length // Returns null if nullableString is null

// Elvis operator
val lengthOrZero = nullableString?.length ?: 0

// Not-null assertion (use carefully!)
val lengthForced = nullableString!!.length // Throws NPE if null

// Safe casting
val obj: Any = "Hello"
val str = obj as? String // Safe cast, returns null if cast fails

// Let function for null checks
nullableString?.let {
    println("String is: $it")
    println("Length: ${it.length}")
}

// Run function
nullableString?.run {
    println("String length: $length")
}

// Also function
nullableString?.also {
    println("Processing: $it")
}

// TakeIf and TakeUnless
val result = nullableString?.takeIf { it.length > 0 }
val result2 = nullableString?.takeUnless { it.isNullOrEmpty() }
Beginner
10. What are control flow statements in Kotlin?

Kotlin provides standard control flow statements including if, when, for, while, and do-while loops.

  • If-else: Can be used as expression
  • When: Switch replacement with powerful features
  • For loops: for (i in 1..10)
  • While loops: while (condition)
  • Do-while: do while (condition)
kotlin
// Control Flow in Kotlin
// If-else expression
val age = 25
val status = if (age < 18) "Minor" else "Adult"
println(status)

// When expression (switch replacement)
val grade = 'A'
val result = when (grade) {
    'A' -> "Excellent"
    'B' -> "Good"
    'C' -> "Fair"
    else -> "Needs Improvement"
}
println(result)

// When with ranges
val score = 85
val grade2 = when (score) {
    in 90..100 -> "A"
    in 80..89 -> "B"
    in 70..79 -> "C"
    else -> "F"
}

// For loop
for (i in 1..5) {
    println(i)
}

// For loop with step
for (i in 1..10 step 2) {
    println(i)
}

// For loop down to
for (i in 10 downTo 1) {
    println(i)
}

// While loop
var i = 0
while (i < 5) {
    println(i)
    i++
}

// Do-while loop
do {
    println(i)
    i--
} while (i > 0)
Beginner
11. What are classes and inheritance in Kotlin?

Kotlin supports object-oriented programming with classes, inheritance, and interfaces. Classes are final by default unless marked open.

  • Class: class Person(val name: String)
  • Inheritance: open class Animal
  • Override: override fun method()
  • Interfaces: interface Flyable
  • Abstract classes: abstract class Vehicle
kotlin
// Classes and Inheritance in Kotlin
// Base class
open class Animal(val name: String) {
    open fun makeSound() {
        println("Animal sound")
    }
}

// Derived class
class Dog(name: String, val breed: String) : Animal(name) {
    override fun makeSound() {
        println("Woof!")
    }
}

// Abstract class
abstract class Vehicle {
    abstract fun start()
    fun stop() {
        println("Stopped")
    }
}

// Interface
interface Flyable {
    fun fly()
    fun land() {
        println("Landing...")
    }
}

// Multiple interfaces
interface Swimmable {
    fun swim()
}

class Duck : Flyable, Swimmable {
    override fun fly() {
        println("Flying")
    }
    override fun swim() {
        println("Swimming")
    }
}

// Usage
val dog = Dog("Rex", "German Shepherd")
dog.makeSound()
println(dog.name)

val duck = Duck()
duck.fly()
duck.swim()
Intermediate
12. What are properties in Kotlin?

Properties in Kotlin are similar to fields but with built-in getters and setters. They can be read-only (val) or mutable (var).

  • Backing field: field keyword
  • Custom getter: get() = field.uppercase()
  • Custom setter: set(value) { field = value }
  • Lateinit: lateinit var name: String
  • Lazy: val data by lazy { }
kotlin
// Properties in Kotlin
class Person {
    // Property with getter and setter
    var name: String = ""
        get() = field.uppercase()
        set(value) {
            field = value.trim()
        }

    // Read-only property
    val fullName: String
        get() = "$firstName $lastName"

    // Late initialization
    lateinit var address: String

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

    // Backing field
    var age: Int = 0
        set(value) {
            if (value >= 0) {
                field = value
            }
        }

    // Property delegate
    var email: String by Delegates.observable("") { prop, old, new ->
        println("Email changed from $old to $new")
    }
}

// Usage
val person = Person()
person.name = "  Alice  "
println(person.name) // ALICE
println(person.expensiveData) // Computed lazily
person.age = 25
person.email = "alice@example.com"
Intermediate
13. What are companion objects in Kotlin?

Companion objects are used to define class-level members similar to static members in Java. They can have properties and functions.

  • Declaration: companion object { }
  • Constants: const val TAG = "Class"
  • Factory: Can be used as factory for instances
  • Named: companion object Factory
  • Interfaces: Can implement interfaces
kotlin
// Companion Objects in Kotlin
class MyClass {
    companion object {
        const val TAG = "MyClass"
        var counter = 0
        
        fun create(): MyClass {
            return MyClass()
        }
    }
}

// Companion object with name
class AnotherClass {
    companion object Factory {
        fun create(): AnotherClass = AnotherClass()
    }
}

// Companion object in interface
interface Factory<T> {
    fun create(): T
}

class Product {
    companion object : Factory<Product> {
        override fun create(): Product = Product()
    }
}

// Usage
println(MyClass.TAG)
MyClass.counter++
val instance = MyClass.create()
val another = AnotherClass.Factory.create()
val product = Product.create()
Intermediate
14. How to handle exceptions in Kotlin?

Kotlin uses try-catch-finally blocks for exception handling. It also provides the use function for resource management.

  • Try-catch: try { } catch (e: Exception) { }
  • Try as expression: val result = try { } catch { }
  • Custom exceptions: class MyException : Exception()
  • Finally: finally { }
  • Use function: writer.use { } for auto-closable
kotlin
// Exception Handling in Kotlin
// Try-catch block
fun divide(a: Int, b: Int): Int {
    return try {
        a / b
    } catch (e: ArithmeticException) {
        println("Division by zero!")
        0
    }
}

// Try as expression
val result = try {
    val x = 10 / 0
    "Success"
} catch (e: Exception) {
    "Error: ${e.message}"
}

// Custom exception
class InvalidAgeException(message: String) : Exception(message)

fun validateAge(age: Int) {
    if (age < 0 || age > 150) {
        throw InvalidAgeException("Invalid age: $age")
    }
}

// Finally block
fun readFile() {
    try {
        println("Reading file...")
        // File operations
    } catch (e: Exception) {
        println("Error reading file: ${e.message}")
    } finally {
        println("Closing resources...")
    }
}

// Use function for auto-closable resources
fun writeToFile() {
    val writer = java.io.FileWriter("test.txt")
    writer.use {
        it.write("Hello Kotlin")
    }
}

// Usage
println(divide(10, 2))
println(divide(10, 0))
try {
    validateAge(200)
} catch (e: InvalidAgeException) {
    println(e.message)
}
Intermediate
15. What are lambda expressions in Kotlin?

Lambda expressions are anonymous functions that can be treated as values. They are widely used in functional programming and collections.

  • Syntax: { x -> x * x }
  • it keyword: { it * 2 } for single parameter
  • Higher-order: Functions taking lambdas
  • Function reference: ::functionName
  • Return: Last expression is the return value
kotlin
// Lambda Expressions in Kotlin
// Basic lambda
val square: (Int) -> Int = { x -> x * x }
val square2 = { x: Int -> x * x }

// Lambda with it
val doubled = { x: Int -> x * 2 }
val doubled2: (Int) -> Int = { it * 2 }

// Higher-order functions
fun performOperation(x: Int, y: Int, operation: (Int, Int) -> Int): Int {
    return operation(x, y)
}

// Lambda with multiple lines
val complexOperation = { x: Int ->
    val y = x * 2
    y + 10
}

// Function reference
fun multiply(x: Int, y: Int) = x * y
val multiplyRef = ::multiply

// Returning lambda from function
fun getOperation(type: String): (Int, Int) -> Int {
    return when (type) {
        "add" -> { a, b -> a + b }
        "subtract" -> { a, b -> a - b }
        else -> { _, _ -> 0 }
    }
}

// Usage
println(square(5))
println(performOperation(10, 20, { x, y -> x * y }))
val add = getOperation("add")
println(add(5, 3))
Intermediate
16. What are scope functions in Kotlin?

Scope functions (let, run, with, apply, also) provide a way to execute a block of code in the context of an object.

  • let: Executes block on non-null object
  • run: Executes block and returns result
  • with: Calls functions on object without name
  • apply: Configures object and returns it
  • also: Performs additional operations
kotlin
// Scope Functions in Kotlin
data class Person(var name: String, var age: Int, var city: String)

// let - execute block on non-null object
val person: Person? = Person("Alice", 25, "NYC")
person?.let {
    println("Name: ${it.name}")
    it.age = 26
}

// run - execute block and return result
val result = person?.run {
    this.age = 27
    "Updated age to $age"
}
println(result)

// with - calls functions on object without specifying name
val numbers = mutableListOf(1, 2, 3)
val sum = with(numbers) {
    add(4)
    add(5)
    sum()
}
println("Sum: $sum")

// apply - configure object and return it
val updatedPerson = Person("Bob", 30, "LA").apply {
    age = 31
    city = "SF"
}
println(updatedPerson)

// also - perform additional operations
val numbers2 = mutableListOf(1, 2, 3)
numbers2.also {
    println("Before: $it")
}.add(4)
println("After: $numbers2")

// takeIf - return object if condition true
val adult = person?.takeIf { it.age >= 18 }
println(adult)

// takeUnless - return object if condition false
val minor = person?.takeUnless { it.age >= 18 }
println(minor)
Intermediate
17. What are extension functions in Kotlin?

Extension functions allow you to add new functionality to existing classes without modifying their source code.

  • Declaration: fun String.isEmail(): Boolean
  • Receiver: this refers to the instance
  • Extension properties: val String.wordCount
  • Generic extensions: fun <T> List<T>.custom()
  • Null extensions: fun String?.isNullOrBlank()
kotlin
// Extension Functions in Kotlin
// Extension function for String
fun String.isEmail(): Boolean {
    return this.contains("@") && this.contains(".")
}

fun String.addPrefix(prefix: String): String {
    return "$prefix$this"
}

// Extension function with receiver
fun Int.isEven(): Boolean = this % 2 == 0
fun Int.isOdd(): Boolean = this % 2 != 0

// Extension property
val String.wordCount: Int
    get() = this.split(" ").size

// Extension function with generic
fun <T> List<T>.secondOrNull(): T? {
    return if (this.size >= 2) this[1] else null
}

// Nullable extension
fun String?.isNullOrBlank(): Boolean {
    return this == null || this.isBlank()
}

// Extension for List
fun <T> List<T>.customFilter(predicate: (T) -> Boolean): List<T> {
    return this.filter(predicate)
}

// Usage
val email = "test@example.com"
println(email.isEmail()) // true
println("Hello".addPrefix("Greeting: "))
println(5.isEven()) // false
println("Hello World".wordCount) // 2
val list = listOf(1, 2, 3)
println(list.secondOrNull()) // 2
Intermediate
18. What are type aliases in Kotlin?

Type aliases provide alternative names for existing types, improving readability and reducing repetition.

  • Function types: typealias Operation = (Int, Int) -> Int
  • Complex types: typealias UserMap = Map<String, User>
  • Generic types: Can be used with generics
  • Nested classes: typealias InnerAlias = Inner
  • Readability: Makes code more readable
kotlin
// Type Aliases in Kotlin
// Type alias for function type
typealias Operation = (Int, Int) -> Int
typealias StringPredicate = (String) -> Boolean

// Type alias for complex types
typealias UserMap = Map<String, User>
typealias ResultCallback = (Result<String>) -> Unit

// Usage
val add: Operation = { a, b -> a + b }
val multiply: Operation = { a, b -> a * b }

fun execute(op: Operation, a: Int, b: Int): Int {
    return op(a, b)
}

typealias User = Pair<String, Int>
val users: UserMap = mapOf(
    "user1" to Pair("Alice", 25),
    "user2" to Pair("Bob", 30)
)

// Type alias for nested classes
class Outer {
    inner class Inner
    typealias InnerAlias = Inner
}

// Usage
println(execute(add, 5, 3))
println(execute(multiply, 5, 3))
println(users["user1"]?.first)
Intermediate
19. What are inline functions in Kotlin?

Inline functions are functions that are expanded at the call site, reducing performance overhead of lambda expressions.

  • Declaration: inline fun measureTime(block: () -> Unit)
  • Crossinline: Prevents non-local returns
  • Noinline: Prevents inlining of lambda
  • Reified: inline fun <reified T> isType()
  • Performance: Reduces overhead of lambdas
kotlin
// Inline Functions in Kotlin
// Inline function to reduce overhead
inline fun measureTime(block: () -> Unit) {
    val start = System.currentTimeMillis()
    block()
    val end = System.currentTimeMillis()
    println("Time: ${end - start}ms")
}

// Crossinline - prevents non-local returns
inline fun withCrossinline(crossinline block: () -> Unit) {
    run {
        block()
    }
}

// Noinline - prevents inlining of lambda
inline fun withNoInline(noinline block: () -> Unit) {
    block()
}

// Reified type parameter
inline fun <reified T> isType(value: Any): Boolean {
    return value is T
}

// Usage
measureTime {
    Thread.sleep(100)
}

class MyClass {
    fun test() {
        withCrossinline {
            println("Crossinline")
        }
    }
}

println(isType<String>("Hello")) // true
println(isType<Int>("Hello")) // false

// Inline function with reified for type-safe casting
inline fun <reified T> List<*>.filterIsInstance(): List<T> {
    return this.filter { it is T }.map { it as T }
}

val mixed = listOf(1, "Hello", 3.14, "World")
val strings = mixed.filterIsInstance<String>()
println(strings) // [Hello, World]
Intermediate
20. What are higher-order functions in Kotlin?

Higher-order functions are functions that take other functions as parameters or return functions as results.

  • Parameter: fun operate(op: (Int, Int) -> Int)
  • Return: fun getMultiplier(): (Int) -> Int
  • Function composition: compose(f, g)
  • Callbacks: Used in event handling
  • Functional programming: Core concept in FP
kotlin
// Higher-Order Functions in Kotlin
// Function that takes a function as parameter
fun applyOperation(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

// Function that returns a function
fun getMultiplier(factor: Int): (Int) -> Int {
    return { it * factor }
}

// Function composition
fun <A, B, C> compose(f: (B) -> C, g: (A) -> B): (A) -> C {
    return { x -> f(g(x)) }
}

// Higher-order function with multiple lambdas
fun process(
    value: Int,
    transform: (Int) -> Int,
    filter: (Int) -> Boolean
): Int? {
    return if (filter(value)) transform(value) else null
}

// Usage with lambda
val result = applyOperation(10, 20) { a, b -> a + b }
println(result)

val double = getMultiplier(2)
println(double(5)) // 10

val square = { x: Int -> x * x }
val addTen = { x: Int -> x + 10 }
val squareThenAddTen = compose(addTen, square)
println(squareThenAddTen(5)) // 35

// Using with named function
fun add(a: Int, b: Int) = a + b
val result2 = applyOperation(10, 20, ::add)
println(result2)
Advanced
21. What are coroutines in Kotlin?

Coroutines are lightweight threads for asynchronous programming in Kotlin. They provide structured concurrency without the overhead of threads.

  • Lightweight: Lower overhead than threads
  • Suspending: suspend fun
  • Structured concurrency: Automatic cleanup
  • Dispatchers: Dispatchers.IO, Dispatchers.Default
  • Scopes: runBlocking, coroutineScope
kotlin
// Coroutines in Kotlin
import kotlinx.coroutines.*

// Basic coroutine
suspend fun fetchData(): String {
    delay(1000) // Simulate network call
    return "Data loaded"
}

// Launch coroutine
fun mainLaunch() = runBlocking {
    val job = launch {
        delay(2000)
        println("Coroutine completed")
    }
    job.join()
}

// Async/await
fun mainAsync() = runBlocking {
    val deferred = async {
        fetchData()
    }
    val result = deferred.await()
    println(result)
}

// Parallel coroutines
suspend fun parallelTasks() = coroutineScope {
    val task1 = async { fetchData() }
    val task2 = async { fetchData() }
    val results = listOf(task1.await(), task2.await())
    println("Results: $results")
}

// Coroutine with timeout
suspend fun withTimeoutExample() {
    try {
        withTimeout(1000) {
            delay(2000)
        }
    } catch (e: TimeoutCancellationException) {
        println("Timed out!")
    }
}

// Structured concurrency
fun structuredConcurrency() = runBlocking {
    coroutineScope {
        launch {
            delay(1000)
            println("Task 1")
        }
        launch {
            delay(500)
            println("Task 2")
        }
    }
    println("All tasks completed")
}
Advanced
22. What are flows in Kotlin?

Flows are cold streams of data that emit values over time. They are part of the coroutine library and support reactive programming.

  • Cold streams: Emit values only when collected
  • Operators: filter, map, buffer
  • StateFlow: State holder with current value
  • SharedFlow: Hot stream that can be shared
  • Collect: flow.collect { value -> }
kotlin
// Flows in Kotlin
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

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

// Flow operators
fun processFlow() = runBlocking {
    simpleFlow()
        .filter { it % 2 == 0 }
        .map { "Number: $it" }
        .collect { println(it) }
}

// StateFlow
class StateFlowExample {
    private val _state = MutableStateFlow(0)
    val state: StateFlow<Int> = _state.asStateFlow()

    fun increment() {
        _state.value++
    }
}

// SharedFlow
class SharedFlowExample {
    private val _events = MutableSharedFlow<String>()
    val events: SharedFlow<String> = _events.asSharedFlow()

    suspend fun emitEvent(event: String) {
        _events.emit(event)
    }
}

// Flow with buffer
fun bufferedFlow() = flow {
    for (i in 1..10) {
        emit(i)
    }
}.buffer()

// Flow with exception handling
fun safeFlow() = flow {
    emit(1)
    throw RuntimeException("Error")
}.catch { e ->
    emit(-1)
    println("Caught: ${e.message}")
}
Advanced
23. What are channels in Kotlin?

Channels are communication primitives for coroutines, allowing safe communication between different coroutines.

  • Send/Receive: channel.send(), channel.receive()
  • Buffered: Channel(capacity = 10)
  • Produce: produce { } builder
  • Actor: actor { } for state management
  • Close: channel.close()
kotlin
// Channels in Kotlin
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*

// Basic channel
fun basicChannel() = runBlocking {
    val channel = Channel<String>()
    launch {
        channel.send("Hello")
        channel.send("World")
        channel.close()
    }
    for (msg in channel) {
        println(msg)
    }
}

// Buffered channel
fun bufferedChannel() = runBlocking {
    val channel = Channel<Int>(capacity = 3)
    launch {
        for (i in 1..5) {
            channel.send(i)
            println("Sent: $i")
        }
        channel.close()
    }
    for (value in channel) {
        println("Received: $value")
        delay(100)
    }
}

// Produce coroutine
fun produceExample() = runBlocking {
    val numbers = produce {
        for (i in 1..5) {
            send(i)
            delay(100)
        }
    }
    for (num in numbers) {
        println(num)
    }
}

// Actor pattern
sealed class CounterMsg {
    object Increment : CounterMsg()
    object Decrement : CounterMsg()
    class Get(val response: CompletableDeferred<Int>) : CounterMsg()
}

fun counterActor() = runBlocking {
    val actor = actor<CounterMsg> {
        var count = 0
        for (msg in channel) {
            when (msg) {
                is CounterMsg.Increment -> count++
                is CounterMsg.Decrement -> count--
                is CounterMsg.Get -> msg.response.complete(count)
            }
        }
    }
    actor.send(CounterMsg.Increment)
    actor.send(CounterMsg.Increment)
    val response = CompletableDeferred<Int>()
    actor.send(CounterMsg.Get(response))
    println(response.await()) // 2
}
Advanced
24. What are sealed classes and enum classes?

Sealed classes and enum classes represent restricted hierarchies. Sealed classes are more flexible with multiple instances per type.

  • Enum: Fixed set of constants
  • Sealed: Restricted hierarchy
  • Enum properties: Can have properties
  • Sealed subclasses: Can be data classes
  • When exhaustive: Both work with when expressions
kotlin
// Sealed Classes and Enum Classes
// Enum class
enum class Color {
    RED, GREEN, BLUE
}

enum class Status(val code: Int) {
    SUCCESS(200),
    ERROR(500),
    LOADING(100)
}

// Sealed class for state management
sealed class UiState {
    data class Success(val data: String) : UiState()
    data class Error(val message: String) : UiState()
    object Loading : UiState()
    object Idle : UiState()
}

// Sealed class with properties
sealed class Payment {
    data class Cash(val amount: Double) : Payment()
    data class CreditCard(val number: String, val expiry: String) : Payment()
    data class PayPal(val email: String) : Payment()
}

// Usage
fun handleState(state: UiState) {
    when (state) {
        is UiState.Success -> println("Data: ${state.data}")
        is UiState.Error -> println("Error: ${state.message}")
        UiState.Loading -> println("Loading...")
        UiState.Idle -> println("Idle")
    }
}

fun handlePayment(payment: Payment) {
    when (payment) {
        is Payment.Cash -> println("Cash amount: ${payment.amount}")
        is Payment.CreditCard -> println("Card: ${payment.number}")
        is Payment.PayPal -> println("PayPal: ${payment.email}")
    }
}
Advanced
25. What are generics in Kotlin?

Generics allow you to write type-safe code that works with multiple types. They support variance, constraints, and reified types.

  • Generic classes: class Box<T>(val value: T)
  • Generic functions: fun <T> swap(a: T, b: T)
  • Constraints: <T : Number>
  • Variance: out (covariant), in (contravariant)
  • Reified: inline fun <reified T>
kotlin
// Generics in Kotlin
// Generic class
class Box<T>(val value: T) {
    fun getValue(): T = value
}

// Generic function
fun <T> swap(first: T, second: T): Pair<T, T> {
    return Pair(second, first)
}

// Generic with constraints
fun <T : Number> sum(vararg items: T): Double {
    return items.map { it.toDouble() }.sum()
}

// Generic with multiple constraints
fun <T> process(value: T) where T : CharSequence, T : Comparable<T> {
    println("Length: ${value.length}")
    println("First char: ${value[0]}")
}

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

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

// Invariant
interface Transformer<T> {
    fun transform(input: T): T
}

// Usage
val box = Box("Hello")
println(box.getValue())

val (a, b) = swap(1, 2)
println("$a, $b")

println(sum(1, 2, 3, 4, 5))
Advanced
26. What is delegation in Kotlin?

Delegation in Kotlin allows you to delegate implementation to another object or property. It's a powerful design pattern.

  • Class delegation: class C : A by delegate
  • Property delegation: val lazy: T by lazy { }
  • Observable: var name by Delegates.observable()
  • Vetoable: Validation before property change
  • Custom delegates: Implement ReadWriteProperty
kotlin
// Delegation in Kotlin
// Class delegation
interface Repository {
    fun getData(): String
    fun saveData(data: String)
}

class DatabaseRepository : Repository {
    override fun getData() = "Data from database"
    override fun saveData(data: String) {
        println("Saving to database: $data")
    }
}

class CachedRepository(private val repo: Repository) : Repository by repo {
    private var cache: String? = null

    override fun getData(): String {
        return cache ?: repo.getData().also { cache = it }
    }
}

// Property delegation
class LazyProperty {
    val expensiveValue: String by lazy {
        println("Computing...")
        "Result"
    }
}

// Observable property
class ObservableProperty {
    var name: String by Delegates.observable("Initial") { prop, old, new ->
        println("$old -> $new")
    }
}

// Vetoable property
class VetoableProperty {
    var age: Int by Delegates.vetoable(0) { _, _, new ->
        new >= 0
    }
}

// Usage
val db = DatabaseRepository()
val cached = CachedRepository(db)
println(cached.getData()) // First call - from database
println(cached.getData()) // Second call - from cache

val lazyProp = LazyProperty()
println(lazyProp.expensiveValue) // Computes
println(lazyProp.expensiveValue) // Returns cached
Advanced
27. What are object declarations and singletons?

Object declarations in Kotlin implement the singleton pattern. They ensure only one instance of the class exists.

  • Singleton: object AppConfig
  • Companion object: Class-level singleton
  • Object expression: Anonymous objects
  • Usage: AppConfig.API_URL
  • Thread-safe: Initialization is thread-safe
kotlin
// Object Declarations and Singletons
// Singleton pattern using object
object AppConfig {
    const val API_URL = "https://api.example.com"
    const val TIMEOUT = 5000

    fun printConfig() {
        println("API URL: $API_URL")
        println("Timeout: $TIMEOUT")
    }
}

// Companion object (singleton per class)
class UserManager {
    companion object {
        private var instance: UserManager? = null

        fun getInstance(): UserManager {
            if (instance == null) {
                instance = UserManager()
            }
            return instance!!
        }
    }

    fun login(username: String) {
        println("User $username logged in")
    }
}

// Object expression (anonymous object)
val greeting = object {
    val message = "Hello"
    fun print() {
        println(message)
    }
}

// Object expression implementing interface
interface ClickListener {
    fun onClick()
}

val clickHandler = object : ClickListener {
    override fun onClick() {
        println("Clicked!")
    }
}

// Usage
println(AppConfig.API_URL)
AppConfig.printConfig()

val manager = UserManager.getInstance()
manager.login("Alice")

greeting.print()
clickHandler.onClick()
Advanced
28. How to create DSL in Kotlin?

Kotlin's features like lambdas, extension functions, and infix notation allow creating type-safe DSLs.

  • Builder pattern: Configure objects with lambdas
  • Infx functions: infix fun
  • Lambda with receiver: T.() -> Unit
  • Scope functions: apply, run
  • Type-safe builders: HTML, XML, Gradle
kotlin
// DSL (Domain Specific Language) in Kotlin
// HTML DSL example
class HTML {
    fun body(block: Body.() -> Unit) {
        val body = Body()
        body.block()
        println(body.render())
    }
}

class Body {
    private val elements = mutableListOf<String>()

    fun h1(text: String) {
        elements.add("<h1>$text</h1>")
    }

    fun p(text: String) {
        elements.add("<p>$text</p>")
    }

    fun render(): String {
        return "<body>${elements.joinToString("")}</body>"
    }
}

// Builder DSL
class UserBuilder {
    var name: String = ""
    var age: Int = 0
    var email: String = ""

    fun build(): User = User(name, age, email)
}

data class User(val name: String, val age: Int, val email: String)

fun user(block: UserBuilder.() -> Unit): User {
    val builder = UserBuilder()
    builder.block()
    return builder.build()
}

// Usage
fun html(block: HTML.() -> Unit) {
    val html = HTML()
    html.block()
}

// DSL usage
html {
    body {
        h1("Welcome to Kotlin DSL")
        p("This is a paragraph")
        p("Another paragraph")
    }
}

// Builder usage
val user = user {
    name = "Alice"
    age = 25
    email = "alice@example.com"
}
println(user)
Advanced
29. What are annotations in Kotlin?

Annotations are metadata attached to code elements. They provide information to the compiler or runtime.

  • Custom annotations: @annotation class MyAnnotation
  • Targets: @Target (CLASS, FUNCTION, PROPERTY)
  • Retention: @Retention (SOURCE, BINARY, RUNTIME)
  • Repeatable: @Repeatable
  • Usage: @MyAnnotation("value")
kotlin
// Annotations in Kotlin
// Custom annotation
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class MyAnnotation(val value: String = "default")

// Annotation with multiple parameters
@Target(AnnotationTarget.PROPERTY)
annotation class FieldInfo(val name: String, val required: Boolean = false)

// Use annotation
@MyAnnotation("test")
class AnnotatedClass {
    @FieldInfo(name = "username", required = true)
    var username: String = ""

    @MyAnnotation("method")
    fun annotatedMethod() {
        println("Annotated method")
    }
}

// Reflection to read annotations
fun readAnnotations() {
    val clazz = AnnotatedClass::class
    clazz.annotations.forEach { annotation ->
        println("Class annotation: $annotation")
    }

    val methods = clazz.declaredFunctions
    methods.forEach { method ->
        method.annotations.forEach { annotation ->
            println("Method annotation: $annotation")
        }
    }
}

// Repeatable annotations
@Repeatable
annotation class Permission(val value: String)

@Permission("read")
@Permission("write")
class SecureClass

// Usage
val annotated = AnnotatedClass()
annotated.annotatedMethod()
readAnnotations()
Advanced
30. How to use reflection in Kotlin?

Reflection allows inspecting classes, properties, and functions at runtime. Kotlin provides both Java and Kotlin reflection.

  • KClass: Person::class
  • Properties: kClass.declaredMemberProperties
  • Functions: kClass.declaredFunctions
  • Call: function.call(obj, args)
  • Constructors: kClass.primaryConstructor
kotlin
// Reflection in Kotlin
import kotlin.reflect.*
import kotlin.reflect.full.*

// Class for reflection examples
data class Person(
    val name: String,
    var age: Int,
    val city: String = "Unknown"
) {
    fun greet(): String {
        return "Hello, my name is $name"
    }

    fun updateAge(newAge: Int) {
        age = newAge
    }
}

// Basic reflection
fun basicReflection() {
    val person = Person("Alice", 25)
    val kClass = person::class

    println("Class name: ${kClass.simpleName}")
    println("Members: ${kClass.members}")
    println("Properties: ${kClass.declaredMemberProperties}")
}

// Accessing properties
fun accessProperties() {
    val person = Person("Alice", 25)
    val kClass = person::class

    kClass.declaredMemberProperties.forEach { prop ->
        println("${prop.name} = ${prop.get(person)}")
    }
}

// Calling functions
fun callFunctions() {
    val person = Person("Alice", 25)
    val kClass = person::class

    val greetFunc = kClass.declaredFunctions.find { it.name == "greet" }
    greetFunc?.call(person)?.let { println(it) }

    val updateFunc = kClass.declaredFunctions.find { it.name == "updateAge" }
    updateFunc?.call(person, 30)
    println("Updated age: ${person.age}")
}

// Create instance
fun createInstance() {
    val kClass = Person::class
    val constructor = kClass.primaryConstructor
    val person = constructor?.call("Bob", 30, "NYC")
    println(person)
}
Advanced
31. What are coroutine contexts and dispatchers?

Coroutine contexts define the environment in which coroutines run. Dispatchers determine which thread pool is used.

  • Dispatchers.Default: CPU-intensive work
  • Dispatchers.IO: I/O operations
  • Dispatchers.Main: Main thread (Android/UI)
  • Dispatchers.Unconfined: Not confined to any thread
  • Custom context: Dispatchers.IO + CoroutineName("MyName")
kotlin
// Coroutine Context and Dispatchers
import kotlinx.coroutines.*

// Different dispatchers
fun dispatcherExample() = runBlocking {
    launch(Dispatchers.Default) {
        println("Default: ${Thread.currentThread().name}")
    }
    launch(Dispatchers.IO) {
        println("IO: ${Thread.currentThread().name}")
    }
    launch(Dispatchers.Main) {
        println("Main: ${Thread.currentThread().name}")
    }
    launch(Dispatchers.Unconfined) {
        println("Unconfined: ${Thread.currentThread().name}")
    }
}

// Custom context
fun customContext() = runBlocking {
    val context = Dispatchers.IO + CoroutineName("Custom")
    launch(context) {
        println("Context: ${coroutineContext[CoroutineName]}")
    }
}

// ThreadLocal with coroutines
val threadLocal = ThreadLocal<String>()
fun threadLocalExample() = runBlocking {
    threadLocal.set("Main")
    withContext(Dispatchers.IO) {
        threadLocal.set("IO")
        println("ThreadLocal: ${threadLocal.get()}")
    }
    println("ThreadLocal restored: ${threadLocal.get()}")
}

// Supervisor job
fun supervisorExample() = runBlocking {
    val supervisor = SupervisorJob()
    val scope = CoroutineScope(Dispatchers.IO + supervisor)

    scope.launch {
        delay(100)
        throw RuntimeException("Error")
    }
    scope.launch {
        delay(200)
        println("Still running")
    }
    delay(500)
}

// Coroutine exception handler
fun exceptionHandlerExample() = runBlocking {
    val handler = CoroutineExceptionHandler { _, exception ->
        println("Caught exception: ${exception.message}")
    }
    val scope = CoroutineScope(Dispatchers.IO + handler)
    scope.launch {
        throw RuntimeException("Test exception")
    }
    delay(100)
}
Advanced
32. How to handle shared mutable state in coroutines?

Shared mutable state in coroutines requires synchronization mechanisms like Mutex, Atomic, or single-threaded dispatchers.

  • Mutex: mutex.withLock
  • Atomic: AtomicInteger
  • Single-threaded: Dispatchers.IO.limitedParallelism(1)
  • Actor: State encapsulated in actor
  • Channels: Communication between coroutines
kotlin
// Shared Mutable State in Coroutines
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*

// Using Mutex for synchronization
class CounterWithMutex {
    private var value = 0
    private val mutex = Mutex()

    suspend fun increment() {
        mutex.withLock {
            value++
        }
    }

    fun getValue(): Int = value
}

// Using Atomic operations
class CounterWithAtomic {
    private val value = java.util.concurrent.atomic.AtomicInteger(0)

    suspend fun increment() {
        value.incrementAndGet()
    }

    fun getValue(): Int = value.get()
}

// Single-threaded dispatcher
class CounterWithSingleThread {
    private val dispatcher = Dispatchers.IO.limitedParallelism(1)
    private var value = 0

    suspend fun increment() {
        withContext(dispatcher) {
            value++
        }
    }

    fun getValue(): Int = value
}

// Usage
fun counterExample() = runBlocking {
    val counter = CounterWithMutex()
    val jobs = List(1000) {
        launch {
            repeat(100) {
                counter.increment()
            }
        }
    }
    jobs.forEach { it.join() }
    println("Final count: ${counter.getValue()}")

    val atomicCounter = CounterWithAtomic()
    val jobs2 = List(1000) {
        launch {
            repeat(100) {
                atomicCounter.increment()
            }
        }
    }
    jobs2.forEach { it.join() }
    println("Atomic count: ${atomicCounter.getValue()}")
}
Advanced
33. What are flow operators and transformations?

Flow operators allow transforming and processing streams of data. They support filtering, mapping, buffering, and flattening.

  • filter: Select specific values
  • map: Transform values
  • buffer: Buffer emissions
  • conflate: Drop intermediate values
  • collectLatest: Cancel previous collection
kotlin
// Flow Operators and Transformations
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

// Basic flow transformation
fun flowTransformExample() = runBlocking {
    (1..10).asFlow()
        .filter { it % 2 == 0 }
        .map { "Number $it" }
        .collect { println(it) }
}

// Flow with buffer
fun flowBufferExample() = runBlocking {
    flow {
        for (i in 1..5) {
            emit(i)
            delay(100)
        }
    }
        .buffer()
        .collect { println(it) }
}

// Flow with conflate
fun flowConflateExample() = runBlocking {
    flow {
        for (i in 1..10) {
            emit(i)
            delay(50)
        }
    }
        .conflate()
        .collect { 
            delay(100)
            println(it) 
        }
}

// Flow with collectLatest
fun flowCollectLatestExample() = runBlocking {
    flow {
        for (i in 1..10) {
            emit(i)
            delay(50)
        }
    }
        .collectLatest { value ->
            println("Processing $value")
            delay(100)
            println("Done $value")
        }
}

// FlatMapConcat, FlatMapMerge, FlatMapLatest
fun flowFlatMapExample() = runBlocking {
    flow {
        emit(1)
        emit(2)
        emit(3)
    }
        .flatMapConcat { value ->
            flow {
                emit("$value-a")
                emit("$value-b")
            }
        }
        .collect { println(it) }
}
Advanced
34. What are coroutine scopes and lifecycle?

Coroutine scopes define the lifecycle of coroutines. They provide structured concurrency and automatic cleanup.

  • CoroutineScope: Interface for scopes
  • lifecycleScope: Android lifecycle scopes
  • viewModelScope: ViewModel scopes
  • GlobalScope: Application-level scope
  • SupervisorJob: Independent job hierarchy
kotlin
// Coroutine Scopes and Lifecycle
import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext

// Custom scope with job
class MyScope : CoroutineScope {
    private val job = SupervisorJob()
    override val coroutineContext: CoroutineContext = Dispatchers.IO + job

    fun cancel() {
        job.cancel()
    }
}

// Lifecycle-aware scope (Android-like)
class LifecycleScope {
    private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())

    fun launchWhenCreated(block: suspend CoroutineScope.() -> Unit): Job {
        return scope.launch(block = block)
    }

    fun onDestroy() {
        scope.cancel()
    }
}

// GlobalScope vs CoroutineScope
fun scopeComparison() = runBlocking {
    // GlobalScope - runs until complete
    GlobalScope.launch {
        delay(1000)
        println("GlobalScope")
    }

    // CoroutineScope - tied to parent
    coroutineScope {
        launch {
            delay(1000)
            println("CoroutineScope")
        }
    }
}

// Scope with timeout
fun timeoutScope() = runBlocking {
    try {
        withTimeout(1000) {
            delay(2000)
        }
    } catch (e: TimeoutCancellationException) {
        println("Timed out")
    }

    // With timeout or null
    val result = withTimeoutOrNull(1000) {
        delay(500)
        "Success"
    }
    println(result ?: "Timed out")
}
Advanced
35. What are SharedFlow and StateFlow?

SharedFlow and StateFlow are hot flows that can be shared between multiple collectors. StateFlow is a special case with a current state.

  • StateFlow: Holds current state
  • SharedFlow: Hot stream, can replay values
  • Replay: MutableSharedFlow(replay = 2)
  • Update: stateFlow.update { it + 1 }
  • Combine: combine(flow1, flow2) { }
kotlin
// SharedFlow and StateFlow Deep Dive
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

// StateFlow with update
class StateFlowExample {
    private val _state = MutableStateFlow(0)
    val state: StateFlow<Int> = _state.asStateFlow()

    fun increment() {
        _state.update { it + 1 }
    }

    fun decrement() {
        _state.value--
    }
}

// SharedFlow with replay
class SharedFlowExample {
    private val _events = MutableSharedFlow<String>(
        replay = 2,
        extraBufferCapacity = 10
    )
    val events: SharedFlow<String> = _events.asSharedFlow()

    suspend fun emitEvent(event: String) {
        _events.emit(event)
    }
}

// StateFlow with distinctUntilChanged
fun distinctExample() = runBlocking {
    MutableStateFlow(0)
        .distinctUntilChanged()
        .collect { println(it) }
}

// SharedFlow with subscribe
fun sharedFlowSubscribe() = runBlocking {
    val sharedFlow = MutableSharedFlow<Int>()

    val job = sharedFlow
        .onEach { println("Received: $it") }
        .launchIn(CoroutineScope(Dispatchers.IO))

    sharedFlow.emit(1)
    sharedFlow.emit(2)
    job.cancel()
}

// StateFlow with combine
fun combineFlows() = runBlocking {
    val flow1 = MutableStateFlow(0)
    val flow2 = MutableStateFlow(0)

    flow1.combine(flow2) { a, b -> a + b }
        .collect { println("Sum: $it") }

    flow1.value = 10
    flow2.value = 5
}
Advanced
36. How to handle exceptions in coroutines?

Coroutine exception handling uses try-catch, CoroutineExceptionHandler, and supervisor scopes for error isolation.

  • Try-catch: try catch (e: Exception)
  • ExceptionHandler: CoroutineExceptionHandler
  • SupervisorJob: Isolates child failures
  • Flow catch: .catch
  • SupervisorScope: supervisorScope
kotlin
// Coroutine Exception Handling
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

// Try-catch in coroutine
fun tryCatchExample() = runBlocking {
    try {
        launch {
            throw RuntimeException("Error")
        }
    } catch (e: Exception) {
        println("Caught: ${e.message}")
    }
}

// CoroutineExceptionHandler
fun exceptionHandler() = runBlocking {
    val handler = CoroutineExceptionHandler { _, exception ->
        println("Handler caught: ${exception.message}")
    }
    val scope = CoroutineScope(Dispatchers.IO + handler)
    scope.launch {
        throw RuntimeException("Test")
    }
    delay(100)
}

// Supervisor job for child isolation
fun supervisorExample2() = runBlocking {
    val supervisor = SupervisorJob()
    val scope = CoroutineScope(Dispatchers.IO + supervisor)

    scope.launch {
        try {
            delay(100)
            throw RuntimeException("Child 1 error")
        } catch (e: Exception) {
            println("Child 1 caught: ${e.message}")
        }
    }

    scope.launch {
        delay(200)
        println("Child 2 still running")
    }

    delay(500)
}

// Flow exception handling
fun flowException() = runBlocking {
    flow {
        emit(1)
        throw RuntimeException("Flow error")
    }
        .catch { e ->
            println("Flow caught: ${e.message}")
            emit(-1)
        }
        .collect { println(it) }
}

// SupervisorScope
fun supervisorScopeExample() = runBlocking {
    supervisorScope {
        launch {
            throw RuntimeException("Error")
        }
        launch {
            delay(100)
            println("Still running")
        }
    }
}
Advanced
37. What are channel producers and consumer patterns?

Channel patterns include producer-consumer, fan-out, and fan-in. These enable communication between coroutines.

  • Producer-consumer: Single producer, single consumer
  • Fan-out: Multiple consumers from one channel
  • Fan-in: Multiple producers to one channel
  • Pipelines: Chaining producers and consumers
  • Buffer: Channel capacity for buffering
kotlin
// Channel Producers and Consumer Patterns
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*

// Producer-Consumer pattern
fun producerConsumer() = runBlocking {
    val channel = Channel<Int>(capacity = 10)

    // Producer
    val producer = launch {
        for (i in 1..20) {
            channel.send(i)
            println("Produced: $i")
            delay(100)
        }
        channel.close()
    }

    // Consumer
    val consumer = launch {
        for (value in channel) {
            println("Consumed: $value")
            delay(150)
        }
    }

    producer.join()
    consumer.join()
}

// Fan-out pattern
fun fanOutExample() = runBlocking {
    val channel = Channel<Int>(capacity = 10)

    // Producer
    launch {
        for (i in 1..20) {
            channel.send(i)
        }
        channel.close()
    }

    // Multiple consumers
    repeat(3) { id ->
        launch {
            for (value in channel) {
                println("Consumer $id: $value")
                delay(100)
            }
        }
    }
}

// Fan-in pattern
fun fanInExample() = runBlocking {
    val channel = Channel<String>(capacity = 10)

    // Multiple producers
    repeat(3) { id ->
        launch {
            for (i in 1..5) {
                channel.send("Producer $id: $i")
                delay(50)
            }
        }
    }

    // Single consumer
    repeat(15) {
        println(channel.receive())
    }
    channel.close()
}
Advanced
38. How to handle coroutine cancellation?

Coroutine cancellation is cooperative. You need to check isActive, use cancellation-aware functions, and handle cleanup.

  • Check isActive: if (!isActive) break
  • Cancel: job.cancel()
  • Finally: finally for cleanup
  • NonCancellable: withContext(NonCancellable)
  • Timeout: withTimeout(1000)
kotlin
// Coroutine Cancellation
import kotlinx.coroutines.*

// Cooperative cancellation
fun cooperativeCancellation() = runBlocking {
    val job = launch {
        var i = 0
        while (i < 100) {
            if (!isActive) break
            println("Working: $i")
            i++
            delay(50)
        }
    }
    delay(200)
    job.cancel()
}

// Cancellation with finally
fun cancellationFinally() = runBlocking {
    val job = launch {
        try {
            repeat(100) { i ->
                println("Processing: $i")
                delay(100)
            }
        } finally {
            println("Cleaning up")
            // Non-cancellable block
            withContext(NonCancellable) {
                delay(100)
                println("Cleanup done")
            }
        }
    }
    delay(250)
    job.cancel()
}

// Cancellation with timeout
fun cancellationTimeout() = runBlocking {
    try {
        withTimeout(1000) {
            repeat(10) { i ->
                delay(200)
                println("Iteration: $i")
            }
        }
    } catch (e: TimeoutCancellationException) {
        println("Timed out")
    }
}

// Custom cancellation check
fun customCancellation() = runBlocking {
    val job = launch {
        var i = 0
        while (i < 1000 && isActive) {
            if (i % 100 == 0) {
                println("Still running: $i")
            }
            i++
            Thread.sleep(1) // CPU-bound work
        }
    }
    delay(100)
    job.cancel()
    job.join()
}
Advanced
39. How to test coroutines?

Kotlin provides kotlinx-coroutines-test for testing coroutines. It offers test dispatchers and virtual time control.

  • runTest: runTest for test execution
  • advanceTimeBy: Virtual time advancement
  • advanceUntilIdle: Run all pending tasks
  • TestDispatcher: Controlled dispatcher
  • Assertions: Standard test assertions
kotlin
// Testing Coroutines
import kotlinx.coroutines.*
import kotlinx.coroutines.test.*
import kotlin.test.*

// Test dispatcher
class CoroutineTest {
    @Test
    fun testCoroutine() = runTest {
        val result = async {
            delay(1000)
            "Success"
        }.await()
        assertEquals("Success", result)
    }

    @Test
    fun testWithDelay() = runTest {
        var result = ""
        launch {
            delay(1000)
            result = "Done"
        }
        advanceTimeBy(1000)
        assertEquals("Done", result)
    }

    @Test
    fun testMultipleCoroutines() = runTest {
        val results = mutableListOf<String>()
        launch {
            delay(500)
            results.add("Task 1")
        }
        launch {
            delay(300)
            results.add("Task 2")
        }
        advanceUntilIdle()
        assertEquals(listOf("Task 2", "Task 1"), results)
    }

    @Test
    fun testFlow() = runTest {
        val flow = flow {
            emit(1)
            delay(500)
            emit(2)
        }
        val result = flow.toList()
        assertEquals(listOf(1, 2), result)
    }
}

// Test with time control
fun timeControlTest() = runTest {
    var counter = 0
    launch {
        while (true) {
            delay(1000)
            counter++
        }
    }
    advanceTimeBy(3000)
    assertEquals(3, counter)
}
Advanced
40. What is Kotlin Multiplatform?

Kotlin Multiplatform allows sharing code between different platforms (JVM, JS, Native). It's used for cross-platform development.

  • Common code: Shared business logic
  • Platform-specific: Actual implementations
  • Expect/Actual: expect and actual
  • Serialization: Shared serialization logic
  • Coroutines: Shared async code
kotlin
// Kotlin Multiplatform
// Common code
expect fun platformName(): String

fun greet(): String {
    return "Hello from ${platformName()}"
}

// Platform-specific implementations (Android)
// actual fun platformName(): String = "Android"

// Platform-specific implementations (iOS)
// actual fun platformName(): String = "iOS"

// Platform-specific implementations (JS)
// actual fun platformName(): String = "JavaScript"

// Common expect class
expect class Platform() {
    fun getVersion(): String
}

// Common class
class PlatformInfo {
    fun getInfo(): String {
        return "${greet()} version ${Platform().getVersion()}"
    }
}

// Multiplatform with coroutines
expect suspend fun fetchData(): String

fun processData() = runBlocking {
    val data = fetchData()
    println("Data: $data")
}

// Multiplatform with serialization
@Serializable
data class User(
    val id: Int,
    val name: String,
    val email: String
)

expect fun encodeUser(user: User): String
expect fun decodeUser(data: String): User
Coding Round
41. Reverse a string

Reverse a string using the built-in reversed() function or manually.

  • Built-in: str.reversed()
  • StringBuilder: StringBuilder(str).reverse()
  • Manual: Iterate from end to start
  • Complexity: O(n) time
kotlin
// Reverse a string
fun reverseString(str: String): String {
    return str.reversed()
}
println(reverseString("hello")) // "olleh"

// Using StringBuilder
fun reverseStringBuilder(str: String): String {
    return StringBuilder(str).reverse().toString()
}
Coding Round
42. Check palindrome

Check if a string is a palindrome using reversed() or two-pointer approach.

  • Method: cleaned == cleaned.reversed()
  • Two-pointer: Compare from both ends
  • Case insensitive: lowercase()
  • Ignore non-alphanumeric: replace(Regex("[^a-z0-9]"), "")
kotlin
// Check palindrome
fun isPalindrome(str: String): Boolean {
    val cleaned = str.lowercase().replace(Regex("[^a-z0-9]"), "")
    return cleaned == cleaned.reversed()
}
println(isPalindrome("racecar")) // true
println(isPalindrome("hello")) // false

// Two-pointer approach
fun isPalindromeTwoPointer(str: String): Boolean {
    val cleaned = str.lowercase().replace(Regex("[^a-z0-9]"), "")
    var left = 0
    var right = cleaned.length - 1
    while (left < right) {
        if (cleaned[left] != cleaned[right]) return false
        left++
        right--
    }
    return true
}
Coding Round
43. Find max in array

Find maximum value in an array using maxOrNull() or manual iteration.

  • Built-in: arr.maxOrNull()
  • Manual: Iterate and track max
  • Empty array: Handle with require
  • Complexity: O(n) time
kotlin
// Find max in array
fun findMax(arr: IntArray): Int {
    return arr.maxOrNull() ?: throw IllegalArgumentException("Empty array")
}
println(findMax(intArrayOf(1, 5, 3, 9, 2))) // 9

// Manual implementation
fun findMaxManual(arr: IntArray): Int {
    var maxVal = arr[0]
    for (num in arr) {
        if (num > maxVal) maxVal = num
    }
    return maxVal
}
Coding Round
44. Remove duplicates

Remove duplicates from an array using distinct() or toSet().

  • distinct(): arr.distinct()
  • Set: arr.toSet().toList()
  • Order: distinct() preserves order
  • Complexity: O(n) time
kotlin
// Remove duplicates
fun removeDuplicates(arr: List<Int>): List<Int> {
    return arr.distinct()
}
println(removeDuplicates(listOf(1, 2, 2, 3, 3, 4))) // [1, 2, 3, 4]

// Using Set
fun removeDuplicatesSet(arr: List<Int>): List<Int> {
    return arr.toSet().toList()
}
Coding Round
45. Merge arrays

Merge two arrays using + operator or plus() function.

  • Operator: arr1 + arr2
  • plus(): arr1.plus(arr2)
  • Mutable: arr1.addAll(arr2)
  • Unique: union() for unique merge
kotlin
// Merge arrays
fun mergeArrays(arr1: IntArray, arr2: IntArray): IntArray {
    return arr1 + arr2
}
println(mergeArrays(intArrayOf(1, 2), intArrayOf(3, 4)).joinToString()) // [1, 2, 3, 4]

// Alternative
fun mergeArraysPlus(arr1: IntArray, arr2: IntArray): IntArray {
    return arr1.plus(arr2)
}
Coding Round
46. Convert string to number

Convert string to number using toInt() or toIntOrNull() for safe conversion.

  • toInt(): str.toInt()
  • toIntOrNull(): str.toIntOrNull()
  • toDouble(): str.toDouble()
  • Error handling: Use toIntOrNull()
kotlin
// Convert string to number
fun stringToNumber(str: String): Int {
    return str.toInt()
}
println(stringToNumber("42")) // 42

// Safe conversion
fun stringToNumberSafe(str: String): Int? {
    return str.toIntOrNull()
}
Coding Round
47. Loop through map

Iterate through a map using for loop with destructuring.

  • Destructuring: for ((key, value) in map)
  • forEach: map.forEach { (key, value) -> }
  • Keys: map.keys
  • Values: map.values
kotlin
// Loop through map
fun loopMap(map: Map<String, Any>) {
    for ((key, value) in map) {
        println("$key => $value")
    }
}

// Alternative
fun loopMapAlternate(map: Map<String, Any>) {
    map.forEach { (key, value) ->
        println("$key => $value")
    }
}

val data = mapOf("name" to "Alice", "age" to 25, "city" to "NYC")
loopMap(data)
Coding Round
48. Delay function execution

Delay function execution using delay() from coroutines or Thread.sleep().

  • Coroutine: suspend fun delay(ms, block)
  • Thread: Thread.sleep(ms)
  • Async: CoroutineScope with launch
  • Timer: Timer().schedule()
kotlin
// Delay function execution
import kotlinx.coroutines.*

suspend fun delayedExecution(delayMs: Long, block: () -> Unit) {
    delay(delayMs)
    block()
}

// Example usage
suspend fun main() {
    delayedExecution(2000) {
        println("After 2 seconds")
    }
}

// Using Thread.sleep (blocking)
fun delayedExecutionBlocking(delayMs: Long, block: () -> Unit) {
    Thread.sleep(delayMs)
    block()
}
Coding Round
49. HTTP GET request

Make HTTP GET requests using Java's HttpURLConnection or Ktor client.

  • Java: HttpURLConnection
  • Ktor: HttpClient().get(url)
  • Retrofit: Android HTTP client
  • OkHttp: Popular HTTP client
kotlin
// HTTP GET request
import java.net.HttpURLConnection
import java.net.URL

fun fetchData(urlString: String): String {
    val url = URL(urlString)
    val connection = url.openConnection() as HttpURLConnection
    return try {
        connection.inputStream.bufferedReader().readText()
    } finally {
        connection.disconnect()
    }
}

// Using Ktor client (coroutines)
suspend fun fetchDataKtor(url: String): String {
    val client = HttpClient()
    return client.get(url).bodyAsText()
}

// Example
// val data = fetchData("https://api.example.com/data")
Coding Round
50. Create a promise-like Deferred

Create a Deferred using async coroutine builder.

  • async: async
  • await: deferred.await()
  • Error handling: try-catch
  • Chaining: Combine multiple deferreds
kotlin
// Create a promise-like Deferred
import kotlinx.coroutines.*

suspend fun createDeferred(shouldResolve: Boolean): Deferred<String> {
    return GlobalScope.async {
        delay(1000)
        if (shouldResolve) {
            "Success!"
        } else {
            throw Exception("Failed!")
        }
    }
}

// Usage
suspend fun main() {
    val deferred = createDeferred(true)
    try {
        val result = deferred.await()
        println(result)
    } catch (e: Exception) {
        println("Caught: ${e.message}")
    }
}
Coding Round
51. Factorial

Calculate factorial using recursion or iteration.

  • Recursive: if (n <= 1) 1 else n * factorial(n-1)
  • Iterative: Loop from 2 to n
  • Edge cases: 0! = 1, handle negatives
  • Performance: Iterative is faster
kotlin
// Factorial
fun factorial(n: Int): Long {
    return if (n <= 1) 1 else n.toLong() * factorial(n - 1)
}
println(factorial(5)) // 120

// Iterative version
fun factorialIterative(n: Int): Long {
    var result = 1L
    for (i in 2..n) {
        result *= i
    }
    return result
}
Coding Round
52. Fibonacci

Calculate Fibonacci numbers using recursion, iteration, or memoization.

  • Recursive: if (n <= 1) n else fib(n-1) + fib(n-2)
  • Iterative: Loop with variables
  • Memoization: Cache results
  • Complexity: O(2^n) recursive, O(n) iterative
kotlin
// Fibonacci
fun fibonacci(n: Int): Int {
    return if (n <= 1) n else fibonacci(n - 1) + fibonacci(n - 2)
}
println(fibonacci(8)) // 21

// Iterative version
fun fibonacciIterative(n: Int): Int {
    var a = 0
    var b = 1
    for (i in 2..n) {
        val temp = a + b
        a = b
        b = temp
    }
    return if (n > 0) b else a
}
Coding Round
53. FizzBuzz

FizzBuzz prints numbers, replacing multiples of 3 with "Fizz", 5 with "Buzz", and 15 with "FizzBuzz".

  • When expression: when { i % 15 == 0 -> }
  • Order: Check 15 first
  • Range: for (i in 1..n)
  • Common interview: Frequently asked
kotlin
// FizzBuzz
fun fizzBuzz(n: Int) {
    for (i in 1..n) {
        when {
            i % 15 == 0 -> println("FizzBuzz")
            i % 3 == 0 -> println("Fizz")
            i % 5 == 0 -> println("Buzz")
            else -> println(i)
        }
    }
}
fizzBuzz(15)
Coding Round
54. Find missing number

Find missing number using formula n*(n+1)/2 - sum.

  • Formula: total - sum(arr)
  • XOR method: XOR all numbers and indices
  • Edge cases: Empty array, missing first or last
  • Complexity: O(n) time, O(1) space
kotlin
// Find missing number
fun findMissing(arr: IntArray): Int {
    val n = arr.size + 1
    val total = n * (n + 1) / 2
    val sum = arr.sum()
    return total - sum
}
println(findMissing(intArrayOf(1, 2, 4, 5, 6))) // 3
Coding Round
55. Find duplicates

Find duplicates using a Set to track seen elements.

  • Set method: Track seen elements
  • Filter method: arr.filter { it in seen }
  • Group by: arr.groupBy { it }.filter { it.value.size > 1 }
  • Complexity: O(n) time
kotlin
// Find duplicates
fun findDuplicates(arr: List<Int>): Set<Int> {
    val seen = mutableSetOf<Int>()
    val duplicates = mutableSetOf<Int>()
    for (item in arr) {
        if (item in seen) {
            duplicates.add(item)
        } else {
            seen.add(item)
        }
    }
    return duplicates
}
println(findDuplicates(listOf(1, 2, 3, 2, 4, 3))) // [2, 3]
Coding Round
56. Sum of array

Calculate sum using sum() or manual iteration.

  • Built-in: arr.sum()
  • Manual: var sum = 0; for (num in arr) sum += num
  • Reduce: arr.reduce { acc, i -> acc + i }
  • Empty array: Returns 0
kotlin
// Sum of array
fun sumArray(arr: IntArray): Int {
    return arr.sum()
}
println(sumArray(intArrayOf(1, 2, 3, 4, 5))) // 15

// Manual implementation
fun sumArrayManual(arr: IntArray): Int {
    var sum = 0
    for (num in arr) {
        sum += num
    }
    return sum
}
Coding Round
57. Average of array

Calculate average using average() or manual division.

  • Built-in: arr.average()
  • Manual: arr.sum() / arr.size
  • Empty array: Handle with if check
  • Precision: Returns Double
kotlin
// Average of array
fun averageArray(arr: IntArray): Double {
    return arr.average()
}
println(averageArray(intArrayOf(1, 2, 3, 4, 5))) // 3.0

// Manual implementation
fun averageArrayManual(arr: IntArray): Double {
    return arr.sum().toDouble() / arr.size
}
Coding Round
58. Sort array ascending

Sort using sorted() or sort() for in-place sorting.

  • Non-mutating: arr.sorted()
  • Mutating: arr.sort()
  • Custom: arr.sortedBy { it }
  • Complexity: O(n log n)
kotlin
// Sort array ascending
fun sortAscending(arr: IntArray): IntArray {
    return arr.sortedArray()
}
println(sortAscending(intArrayOf(5, 2, 8, 1, 9)).joinToString()) // [1, 2, 5, 8, 9]

// In-place sorting
fun sortAscendingInPlace(arr: IntArray) {
    arr.sort()
}
Coding Round
59. Sort array descending

Sort descending using sortedDescending() or sortDescending().

  • Non-mutating: arr.sortedDescending()
  • Mutating: arr.sortDescending()
  • Custom: arr.sortedBy { -it }
  • Complexity: O(n log n)
kotlin
// Sort array descending
fun sortDescending(arr: IntArray): IntArray {
    return arr.sortedDescending().toIntArray()
}
println(sortDescending(intArrayOf(5, 2, 8, 1, 9)).joinToString()) // [9, 8, 5, 2, 1]

// In-place sorting
fun sortDescendingInPlace(arr: IntArray) {
    arr.sortDescending()
}
Coding Round
60. Flatten nested array

Flatten a nested array using recursion or flatMap.

  • Recursive: Check if element is list
  • flatMap: arr.flatMap { if (it is List<*>) flatten(it) else listOf(it) }
  • Depth: Handle multiple nesting levels
  • Complexity: O(n) time
kotlin
// Flatten nested array
fun flattenArray(arr: List<Any>): List<Any> {
    val result = mutableListOf<Any>()
    for (item in arr) {
        if (item is List<*>) {
            result.addAll(flattenArray(item as List<Any>))
        } else {
            result.add(item)
        }
    }
    return result
}
println(flattenArray(listOf(1, listOf(2, listOf(3, 4), 5), 6))) // [1, 2, 3, 4, 5, 6]

// Using Kotlin's flatMap
fun flattenArrayFlat(arr: List<Any>): List<Any> {
    return arr.flatMap {
        if (it is List<*>) flattenArrayFlat(it as List<Any>) else listOf(it)
    }
}
Coding Round
61. Chunk array

Split array into chunks using chunked() or manual slicing.

  • Built-in: arr.chunked(size)
  • Manual: arr.slice(i until min(i + size, arr.size))
  • Step: for (i in arr.indices step size)
  • Use case: Batch processing
kotlin
// Chunk array
fun chunkArray(arr: List<Int>, size: Int): List<List<Int>> {
    return arr.chunked(size)
}
println(chunkArray(listOf(1, 2, 3, 4, 5, 6), 2)) // [[1, 2], [3, 4], [5, 6]]

// Manual implementation
fun chunkArrayManual(arr: List<Int>, size: Int): List<List<Int>> {
    val result = mutableListOf<List<Int>>()
    for (i in arr.indices step size) {
        result.add(arr.slice(i until minOf(i + size, arr.size)))
    }
    return result
}
Coding Round
63. Quick sort

Quick sort using pivot-based partitioning and recursion.

  • Algorithm: Choose pivot, partition, recurse
  • Time: O(n log n) average, O(n²) worst
  • In-place: Implement in-place for performance
  • Pivot: First, last, or random
kotlin
// Quick sort
fun quickSort(arr: IntArray): IntArray {
    if (arr.size <= 1) return arr
    val pivot = arr[0]
    val left = arr.sliceArray(1 until arr.size).filter { it < pivot }.toIntArray()
    val right = arr.sliceArray(1 until arr.size).filter { it >= pivot }.toIntArray()
    return quickSort(left) + pivot + quickSort(right)
}
println(quickSort(intArrayOf(5, 3, 8, 4, 2, 7, 1, 6)).joinToString())

// In-place quick sort
fun quickSortInPlace(arr: IntArray, low: Int = 0, high: Int = arr.size - 1) {
    if (low < high) {
        val pi = partition(arr, low, high)
        quickSortInPlace(arr, low, pi - 1)
        quickSortInPlace(arr, pi + 1, high)
    }
}

fun partition(arr: IntArray, low: Int, high: Int): Int {
    val pivot = arr[high]
    var i = low - 1
    for (j in low until high) {
        if (arr[j] <= pivot) {
            i++
            arr[i] = arr[j].also { arr[j] = arr[i] }
        }
    }
    arr[i + 1] = arr[high].also { arr[high] = arr[i + 1] }
    return i + 1
}
Coding Round
64. Merge sort

Merge sort using divide-and-conquer and merging.

  • Algorithm: Divide, sort, merge
  • Time: O(n log n)
  • Stable: Maintains relative order
  • Space: O(n) auxiliary space
kotlin
// Merge sort
fun mergeSort(arr: IntArray): IntArray {
    if (arr.size <= 1) return arr
    val mid = arr.size / 2
    val left = mergeSort(arr.sliceArray(0 until mid))
    val right = mergeSort(arr.sliceArray(mid until arr.size))
    return merge(left, right)
}

fun merge(left: IntArray, right: IntArray): IntArray {
    var i = 0
    var j = 0
    val result = mutableListOf<Int>()
    while (i < left.size && j < right.size) {
        if (left[i] <= right[j]) {
            result.add(left[i++])
        } else {
            result.add(right[j++])
        }
    }
    result.addAll(left.sliceArray(i until left.size))
    result.addAll(right.sliceArray(j until right.size))
    return result.toIntArray()
}
Coding Round
65. Bubble sort

Bubble sort with early termination optimization.

  • Algorithm: Compare adjacent, swap if needed
  • Time: O(n²) worst case
  • Optimization: Stop if no swaps
  • Use case: Educational, small datasets
kotlin
// Bubble sort
fun bubbleSort(arr: IntArray): IntArray {
    val sorted = arr.copyOf()
    for (i in 0 until sorted.size - 1) {
        for (j in 0 until sorted.size - 1 - i) {
            if (sorted[j] > sorted[j + 1]) {
                val temp = sorted[j]
                sorted[j] = sorted[j + 1]
                sorted[j + 1] = temp
            }
        }
    }
    return sorted
}

// Optimized bubble sort
fun bubbleSortOptimized(arr: IntArray): IntArray {
    val sorted = arr.copyOf()
    for (i in 0 until sorted.size - 1) {
        var swapped = false
        for (j in 0 until sorted.size - 1 - i) {
            if (sorted[j] > sorted[j + 1]) {
                val temp = sorted[j]
                sorted[j] = sorted[j + 1]
                sorted[j + 1] = temp
                swapped = true
            }
        }
        if (!swapped) break
    }
    return sorted
}
Coding Round
66. Intersection of arrays

Find common elements using filter or intersect.

  • Filter: arr1.filter { it in arr2 }
  • Intersect: arr1.intersect(arr2)
  • Set: arr1.filter { it in set2 }
  • Complexity: O(n²) with in, O(n) with Set
kotlin
// Intersection of arrays
fun intersection(arr1: IntArray, arr2: IntArray): IntArray {
    return arr1.filter { it in arr2 }.toIntArray()
}
println(intersection(intArrayOf(1, 2, 3, 4), intArrayOf(3, 4, 5, 6)).joinToString()) // [3, 4]

// Using Set for efficiency
fun intersectionSet(arr1: IntArray, arr2: IntArray): IntArray {
    val set2 = arr2.toSet()
    return arr1.filter { it in set2 }.toIntArray()
}
Coding Round
67. Union of arrays

Combine arrays with unique elements using union or Set.

  • Union: arr1.union(arr2)
  • Distinct: (arr1 + arr2).distinct()
  • Set: arr1.toSet() + arr2.toSet()
  • Complexity: O(n) time
kotlin
// Union of arrays
fun union(arr1: IntArray, arr2: IntArray): IntArray {
    return (arr1 + arr2).distinct().toIntArray()
}
println(union(intArrayOf(1, 2, 3), intArrayOf(3, 4, 5)).joinToString()) // [1, 2, 3, 4, 5]

// Using Set
fun unionSet(arr1: IntArray, arr2: IntArray): IntArray {
    return (arr1.toSet() + arr2.toSet()).toIntArray()
}
Coding Round
68. Difference of arrays

Find elements in first array not in second using filter.

  • Difference: arr1.filter { it !in arr2 }
  • Symmetric: arr1.subtract(arr2) + arr2.subtract(arr1)
  • Set: Use Set for efficiency
  • Complexity: O(n²) with in, O(n) with Set
kotlin
// Difference of arrays
fun difference(arr1: IntArray, arr2: IntArray): IntArray {
    return arr1.filter { it !in arr2 }.toIntArray()
}
println(difference(intArrayOf(1, 2, 3, 4), intArrayOf(3, 4, 5, 6)).joinToString()) // [1, 2]

// Symmetric difference
fun symmetricDifference(arr1: IntArray, arr2: IntArray): IntArray {
    return (arr1.filter { it !in arr2 } + arr2.filter { it !in arr1 }).toIntArray()
}
Coding Round
69. Group by property

Group objects by property using groupBy.

  • Built-in: items.groupBy { it.type }
  • Custom: Manual grouping with map
  • Multiple keys: Group by multiple properties
  • Complexity: O(n) time
kotlin
// Group by property
data class Item(val type: String, val name: String)

fun groupByProperty(items: List<Item>, key: String): Map<String, List<Item>> {
    return items.groupBy { 
        when (key) {
            "type" -> it.type
            else -> it.name
        }
    }
}

val data = listOf(
    Item("fruit", "apple"),
    Item("fruit", "banana"),
    Item("veg", "carrot")
)
println(groupByProperty(data, "type"))
Coding Round
70. Deep clone object

Deep clone objects by recursively copying all nested structures.

  • Method: Recursive copying
  • Maps: Copy entries recursively
  • Lists: Copy elements recursively
  • Limitations: May not handle all edge cases
kotlin
// Deep clone object
fun <T> deepClone(obj: T): T {
    return try {
        @Suppress("UNCHECKED_CAST")
        when (obj) {
            is Map<*, *> -> {
                val map = mutableMapOf<Any, Any?>()
                obj.forEach { (key, value) ->
                    map[key] = deepClone(value)
                }
                map as T
            }
            is List<*> -> {
                val list = mutableListOf<Any?>()
                obj.forEach { item ->
                    list.add(deepClone(item))
                }
                list as T
            }
            is MutableMap<*, *> -> deepClone(obj.toMap())
            is MutableList<*> -> deepClone(obj.toList())
            else -> obj
        }
    } catch (e: Exception) {
        obj
    }
}

data class User(val name: String, val address: Address)
data class Address(val city: String, val zip: String)

val original = User("Alice", Address("NYC", "10001"))
val cloned = deepClone(original)
// Note: This will copy the entire object structure
Coding Round
71. Immutable update

Perform immutable updates on nested structures by copying at each level.

  • Method: Copy and update path
  • Path: Use dot notation for nested access
  • Libraries: Use for state management
  • Use case: Functional programming
kotlin
// Immutable update
fun <T> updateImmutable(obj: Map<String, Any>, path: String, value: Any): Map<String, Any> {
    val parts = path.split(".")
    if (parts.size == 1) {
        return obj + (parts[0] to value)
    }
    val first = parts[0]
    val rest = parts.drop(1).joinToString(".")
    val nested = obj[first] as? Map<String, Any> ?: emptyMap()
    return obj + (first to updateImmutable(nested, rest, value))
}

val state = mapOf(
    "user" to mapOf(
        "name" to "Alice",
        "age" to 25
    )
)
val newState = updateImmutable(state, "user.age", 26)
println(state["user"]["age"]) // 25
println(newState["user"]["age"]) // 26
Coding Round
72. Pipe function

Pipe composes functions from left to right.

  • Method: pipe(fns...)(value)
  • Implementation: Reduce with function application
  • Use case: Function composition
  • Direction: Left to right
kotlin
// Pipe function
fun <T> pipe(vararg fns: (T) -> T): (T) -> T {
    return { value ->
        var result = value
        for (fn in fns) {
            result = fn(result)
        }
        result
    }
}

val double: (Int) -> Int = { it * 2 }
val addTen: (Int) -> Int = { it + 10 }
val square: (Int) -> Int = { it * it }

val process = pipe(double, addTen, square)
println(process(5)) // (5*2+10)^2 = 400
Coding Round
73. Compose function

Compose functions from right to left.

  • Method: compose(fns...)(value)
  • Implementation: ReduceRight with function application
  • Use case: Function composition
  • Direction: Right to left
kotlin
// Compose function
fun <T> compose(vararg fns: (T) -> T): (T) -> T {
    return { value ->
        var result = value
        for (fn in fns.reversed()) {
            result = fn(result)
        }
        result
    }
}

val process2 = compose(square, addTen, double)
println(process2(5)) // (5*2+10)^2 = 400
Coding Round
74. Memoization

Cache function results based on arguments.

  • Method: Cache in mutable map
  • Key: Use arguments as key
  • Use case: Expensive computations
  • Trade-off: Memory for speed
kotlin
// Memoization
fun <T, R> memoize(fn: (T) -> R): (T) -> R {
    val cache = mutableMapOf<T, R>()
    return { arg ->
        cache.getOrPut(arg) {
            fn(arg)
        }
    }
}

val fibonacciMemo = memoize { n: Int ->
    when {
        n <= 1 -> n
        else -> fibonacciMemo(n - 1) + fibonacciMemo(n - 2)
    }
}
println(fibonacciMemo(10))
Coding Round
75. Once function

Ensure a function is called only once.

  • Method: Use a flag and closure
  • Implementation: Track if called
  • Use case: Initialization
  • Thread safety: Not needed in single-threaded
kotlin
// Once function
fun <T> once(fn: () -> T): () -> T {
    var called = false
    var result: T? = null
    return {
        if (!called) {
            called = true
            result = fn()
        }
        result as T
    }
}

val initialize = once {
    println("Initialized")
    mapOf("id" to 1, "name" to "App")
}

println(initialize()) // Prints "Initialized"
println(initialize()) // Returns cached result
Coding Round
76. Debounce with leading edge

Debounce with leading edge executes immediately then waits.

  • Method: Track last call time
  • Implementation: Immediate execution, cooldown
  • Use case: Save actions, API calls
  • Difference: Leading vs trailing edge
kotlin
// Debounce with leading edge
fun debounceLeading(delayMs: Long, fn: () -> Unit): () -> Unit {
    var lastCall = 0L
    var timer: Thread? = null
    return {
        val now = System.currentTimeMillis()
        if (now - lastCall < delayMs) {
            timer?.interrupt()
            timer = Thread {
                Thread.sleep(delayMs)
                lastCall = System.currentTimeMillis()
                fn()
            }.also { it.start() }
        } else {
            lastCall = now
            fn()
        }
    }
}
Coding Round
77. Throttle with leading edge

Throttle with leading edge executes at most once per time period.

  • Method: Track last call time
  • Implementation: Execute if enough time passed
  • Use case: Scroll events, resize
  • Difference: Leading vs trailing edge
kotlin
// Throttle with leading edge
fun throttleLeading(delayMs: Long, fn: () -> Unit): () -> Unit {
    var lastCall = 0L
    return {
        val now = System.currentTimeMillis()
        if (now - lastCall >= delayMs) {
            lastCall = now
            fn()
        }
    }
}
Coding Round
78. Deep equal

Deep equality comparison for nested structures.

  • Method: Recursive comparison
  • Base cases: Primitive values
  • Maps: Compare entries recursively
  • Lists: Compare elements recursively
kotlin
// Deep equal
fun deepEqual(obj1: Any?, obj2: Any?): Boolean {
    if (obj1 === obj2) return true
    if (obj1 == null || obj2 == null) return false
    if (obj1::class != obj2::class) return false
    
    return when {
        obj1 is Map<*, *> && obj2 is Map<*, *> -> {
            if (obj1.size != obj2.size) return false
            obj1.all { (key, value) ->
                obj2[key]?.let { deepEqual(value, it) } ?: false
            }
        }
        obj1 is List<*> && obj2 is List<*> -> {
            if (obj1.size != obj2.size) return false
            obj1.indices.all { deepEqual(obj1[it], obj2[it]) }
        }
        obj1 is Array<*> && obj2 is Array<*> -> {
            if (obj1.size != obj2.size) return false
            obj1.indices.all { deepEqual(obj1[it], obj2[it]) }
        }
        else -> obj1 == obj2
    }
}
Coding Round
79. Observable pattern

Observable pattern for event notification.

  • Observable: Maintains subscribers
  • Subscribe: Add callback
  • Notify: Call all subscribers
  • Unsubscribe: Remove callback
kotlin
// Observable pattern
class Observable<T> {
    private val subscribers = mutableListOf<(T) -> Unit>()

    fun subscribe(callback: (T) -> Unit): () -> Unit {
        subscribers.add(callback)
        return { subscribers.remove(callback) }
    }

    fun notify(data: T) {
        subscribers.forEach { it(data) }
    }
}

// Usage
val observable = Observable<String>()
val unsubscribe = observable.subscribe { data ->
    println("Received: $data")
}
observable.notify("Hello") // Received: Hello
unsubscribe()
observable.notify("World") // Nothing happens
Coding Round
80. Singleton pattern

Singleton pattern using object declaration.

  • Method: object Singleton
  • Thread-safe: Automatic initialization
  • Global access: Singleton.function()
  • Use case: Configuration, logging
kotlin
// Singleton pattern
object Singleton {
    private val data = mutableMapOf<String, Any>()

    fun set(key: String, value: Any) {
        data[key] = value
    }

    fun get(key: String): Any? {
        return data[key]
    }
}

// Usage
Singleton.set("name", "Alice")
println(Singleton.get("name")) // Alice
Coding Round
81. Factory pattern

Factory pattern for creating objects.

  • Method: Factory class with create method
  • Benefits: Decouples creation
  • Sealed classes: Used with factory
  • Use case: Creating different types
kotlin
// Factory pattern
sealed class User {
    data class Admin(val name: String) : User()
    data class Guest(val name: String) : User()
    data class RegularUser(val name: String) : User()
}

object UserFactory {
    fun createUser(type: String, name: String): User {
        return when (type) {
            "admin" -> User.Admin(name)
            "guest" -> User.Guest(name)
            else -> User.RegularUser(name)
        }
    }
}

// Usage
val admin = UserFactory.createUser("admin", "Alice")
println(admin)
Coding Round
82. Strategy pattern

Strategy pattern for interchangeable algorithms.

  • Interface: Strategy interface
  • Context: Uses strategy
  • Benefits: Runtime switching
  • Use case: Payment methods
kotlin
// Strategy pattern
interface PaymentStrategy {
    fun pay(amount: Double)
}

class CreditCardStrategy : PaymentStrategy {
    override fun pay(amount: Double) {
        println("Paid $$amount with Credit Card")
    }
}

class PayPalStrategy : PaymentStrategy {
    override fun pay(amount: Double) {
        println("Paid $$amount with PayPal")
    }
}

class CryptoStrategy : PaymentStrategy {
    override fun pay(amount: Double) {
        println("Paid $$amount with Crypto")
    }
}

class PaymentContext(var strategy: PaymentStrategy) {
    fun executePayment(amount: Double) {
        strategy.pay(amount)
    }
}

// Usage
val context = PaymentContext(CreditCardStrategy())
context.executePayment(100.0)
context.strategy = PayPalStrategy()
context.executePayment(50.0)
Coding Round
83. Observer pattern

Observer pattern for one-to-many notification.

  • Subject: Maintains observers
  • Observer: Receives updates
  • Benefits: Loose coupling
  • Use case: Event handling
kotlin
// Observer pattern
interface Observer {
    fun update(data: String)
}

class Subject {
    private val observers = mutableListOf<Observer>()
    var state: String = ""
        set(value) {
            field = value
            notifyObservers()
        }

    fun attach(observer: Observer) {
        observers.add(observer)
    }

    fun detach(observer: Observer) {
        observers.remove(observer)
    }

    private fun notifyObservers() {
        observers.forEach { it.update(state) }
    }
}

class ConcreteObserver(private val name: String) : Observer {
    override fun update(data: String) {
        println("$name received: $data")
    }
}

// Usage
val subject = Subject()
val observer1 = ConcreteObserver("Observer1")
val observer2 = ConcreteObserver("Observer2")
subject.attach(observer1)
subject.attach(observer2)
subject.state = "Hello World"
Coding Round
84. Decorator pattern

Decorator pattern for adding behavior.

  • Component: Base interface
  • Decorator: Wraps component
  • Benefits: Flexible extension
  • Use case: Logging, authentication
kotlin
// Decorator pattern
data class Coffee(
    val cost: Double,
    val description: String
)

fun milkDecorator(coffee: Coffee): Coffee {
    return coffee.copy(
        cost = coffee.cost + 2.0,
        description = "${coffee.description}, Milk"
    )
}

fun sugarDecorator(coffee: Coffee): Coffee {
    return coffee.copy(
        cost = coffee.cost + 1.0,
        description = "${coffee.description}, Sugar"
    )
}

// Usage
var coffee = Coffee(5.0, "Coffee")
coffee = milkDecorator(coffee)
coffee = sugarDecorator(coffee)
println(coffee.description) // Coffee, Milk, Sugar
println(coffee.cost) // 8.0
Coding Round
85. Command pattern

Command pattern for encapsulating requests.

  • Command: Encapsulates request
  • Invoker: Executes commands
  • Receiver: Performs work
  • Benefits: Undo/redo, queuing
kotlin
// Command pattern
interface Command {
    fun execute()
    fun undo()
}

class AddCommand(private val receiver: MutableList<Int>, private val value: Int) : Command {
    override fun execute() {
        receiver.add(value)
    }
    override fun undo() {
        receiver.remove(value)
    }
}

// Usage
val receiver = mutableListOf(1, 2, 3)
val cmd = AddCommand(receiver, 4)
cmd.execute()
println(receiver) // [1, 2, 3, 4]
cmd.undo()
println(receiver) // [1, 2, 3]
Coding Round
86. Memento pattern

Memento pattern for state capture and restoration.

  • Originator: Creates/restores mementos
  • Memento: Stores internal state
  • Caretaker: Manages mementos
  • Benefits: Undo/redo
kotlin
// Memento pattern
data class Memento(val state: String)

class Originator {
    var state: String = ""
        set(value) {
            field = value
            println("State set to: $value")
        }

    fun saveState(): Memento = Memento(state)
    fun restoreState(memento: Memento) {
        state = memento.state
    }
}

class Caretaker {
    private val mementos = mutableListOf<Memento>()

    fun addMemento(memento: Memento) {
        mementos.add(memento)
    }

    fun getMemento(index: Int): Memento = mementos[index]
}

// Usage
val originator = Originator()
val caretaker = Caretaker()

originator.state = "State 1"
caretaker.addMemento(originator.saveState())
originator.state = "State 2"
caretaker.addMemento(originator.saveState())
originator.state = "State 3"

originator.restoreState(caretaker.getMemento(0))
println(originator.state) // State 1
Coding Round
87. Mediator pattern

Mediator pattern for centralized communication.

  • Mediator: Encapsulates communication
  • Colleague: Communicates through mediator
  • Benefits: Loose coupling
  • Use case: Chat systems
kotlin
// Mediator pattern
class Mediator {
    private val colleagues = mutableListOf<Colleague>()

    fun register(colleague: Colleague) {
        colleagues.add(colleague)
    }

    fun send(message: String, sender: Colleague) {
        colleagues.filter { it != sender }.forEach { 
            it.receive(message) 
        }
    }
}

class Colleague(
    val name: String,
    private val mediator: Mediator
) {
    init {
        mediator.register(this)
    }

    fun send(message: String) {
        mediator.send(message, this)
    }

    fun receive(message: String) {
        println("$name received: $message")
    }
}

// Usage
val mediator = Mediator()
val alice = Colleague("Alice", mediator)
val bob = Colleague("Bob", mediator)
alice.send("Hello Bob!")
Coding Round
88. Chain of Responsibility

Chain of Responsibility for processing requests.

  • Handler: Processes or forwards
  • Chain: Linked list of handlers
  • Benefits: Decoupling
  • Use case: Logging, authentication
kotlin
// Chain of Responsibility
abstract class Handler {
    var nextHandler: Handler? = null

    fun setNext(handler: Handler): Handler {
        nextHandler = handler
        return handler
    }

    abstract fun handle(request: Map<String, Any>)
}

class AuthHandler : Handler() {
    override fun handle(request: Map<String, Any>) {
        if (request.containsKey("token")) {
            println("Authentication passed")
            nextHandler?.handle(request)
        } else {
            println("Authentication failed")
        }
    }
}

class LoggerHandler : Handler() {
    override fun handle(request: Map<String, Any>) {
        println("Logging request: ${request["url"]}")
        nextHandler?.handle(request)
    }
}

// Usage
val auth = AuthHandler()
val logger = LoggerHandler()
auth.setNext(logger)
auth.handle(mapOf("token" to "valid", "url" to "/api"))
Coding Round
89. State pattern

State pattern for changing behavior with state.

  • Context: Maintains state
  • State: Defines behavior
  • Benefits: Clean state management
  • Use case: State machines
kotlin
// State pattern
interface State {
    fun handle()
}

class ReadyState : State {
    override fun handle() {
        println("Ready: Waiting for input")
    }
}

class ProcessingState : State {
    override fun handle() {
        println("Processing: Working on task")
    }
}

class CompletedState : State {
    override fun handle() {
        println("Completed: Task finished")
    }
}

class Context {
    var state: State = ReadyState()
        set(value) {
            field = value
        }

    fun request() {
        state.handle()
    }
}

// Usage
val context = Context()
context.request() // Ready: Waiting for input
context.state = ProcessingState()
context.request() // Processing: Working on task
context.state = CompletedState()
context.request() // Completed: Task finished
Coding Round
90. Proxy pattern

Proxy pattern for controlling access.

  • Subject: Real object
  • Proxy: Controls access
  • Benefits: Access control
  • Use case: Virtual proxies
kotlin
// Proxy pattern
class RealSubject {
    fun request() {
        println("RealSubject: Handling request")
    }
}

class Proxy {
    private var realSubject: RealSubject? = null

    fun request() {
        if (checkAccess()) {
            realSubject = realSubject ?: RealSubject()
            realSubject?.request()
            logAccess()
        }
    }

    private fun checkAccess(): Boolean {
        println("Proxy: Checking access")
        return true
    }

    private fun logAccess() {
        println("Proxy: Logging access")
    }
}

// Usage
val proxy = Proxy()
proxy.request()
Coding Round
91. Flyweight pattern

Flyweight pattern for sharing objects.

  • Flyweight: Shared object
  • Factory: Manages flyweights
  • Benefits: Memory optimization
  • Use case: Text rendering
kotlin
// Flyweight pattern
class Flyweight(private val sharedState: String) {
    fun operation(uniqueState: String) {
        println("Shared: $sharedState, Unique: $uniqueState")
    }
}

class FlyweightFactory {
    private val flyweights = mutableMapOf<String, Flyweight>()

    fun getFlyweight(sharedState: String): Flyweight {
        return flyweights.getOrPut(sharedState) {
            Flyweight(sharedState)
        }
    }
}

// Usage
val factory = FlyweightFactory()
val fw1 = factory.getFlyweight("state1")
val fw2 = factory.getFlyweight("state1")
val fw3 = factory.getFlyweight("state2")
fw1.operation("unique1")
fw2.operation("unique2")
fw3.operation("unique3")
Coding Round
92. Bridge pattern

Bridge pattern for separating abstraction from implementation.

  • Abstraction: High-level interface
  • Implementation: Low-level operations
  • Benefits: Separation of concerns
  • Use case: Cross-platform
kotlin
// Bridge pattern
interface Implementation {
    fun operation()
}

class ConcreteImplementationA : Implementation {
    override fun operation() {
        println("ConcreteImplementationA: Operation")
    }
}

class ConcreteImplementationB : Implementation {
    override fun operation() {
        println("ConcreteImplementationB: Operation")
    }
}

class Abstraction(private var impl: Implementation) {
    fun operation() {
        println("Abstraction: Additional logic")
        impl.operation()
    }
}

// Usage
val implA = ConcreteImplementationA()
val implB = ConcreteImplementationB()
val abstraction1 = Abstraction(implA)
val abstraction2 = Abstraction(implB)
abstraction1.operation()
abstraction2.operation()
Coding Round
93. Adapter pattern

Adapter pattern for converting interfaces.

  • Target: Expected interface
  • Adaptee: Existing interface
  • Adapter: Bridges interfaces
  • Benefits: Reusability
kotlin
// Adapter pattern
class Target {
    fun request() {
        println("Target: Request")
    }
}

class Adaptee {
    fun specificRequest() {
        println("Adaptee: Specific Request")
    }
}

class Adapter(private val adaptee: Adaptee) {
    fun request() {
        adaptee.specificRequest()
    }
}

// Usage
val adaptee = Adaptee()
val adapter = Adapter(adaptee)
adapter.request()
Coding Round
94. Facade pattern

Facade pattern for simplifying subsystems.

  • Facade: Simplified interface
  • Subsystem: Complex components
  • Benefits: Simplified interface
  • Use case: Library APIs
kotlin
// Facade pattern
class SubsystemA {
    fun operationA() {
        println("SubsystemA: Operation")
    }
}

class SubsystemB {
    fun operationB() {
        println("SubsystemB: Operation")
    }
}

class Facade {
    private val subsystemA = SubsystemA()
    private val subsystemB = SubsystemB()

    fun operation() {
        subsystemA.operationA()
        subsystemB.operationB()
        println("Facade: Complex operation")
    }
}

// Usage
val facade = Facade()
facade.operation()
Coding Round
95. Composite pattern

Composite pattern for tree structures.

  • Component: Interface for all
  • Leaf: Individual object
  • Composite: Container
  • Benefits: Uniform interface
kotlin
// Composite pattern
interface Component {
    fun operation()
}

class Leaf(private val name: String) : Component {
    override fun operation() {
        println("Leaf $name: Operation")
    }
}

class Composite(private val name: String) : Component {
    private val children = mutableListOf<Component>()

    fun add(component: Component) {
        children.add(component)
    }

    fun remove(component: Component) {
        children.remove(component)
    }

    override fun operation() {
        println("Composite $name: Operation")
        children.forEach { it.operation() }
    }
}

// Usage
val leaf1 = Leaf("A")
val leaf2 = Leaf("B")
val composite = Composite("Root")
composite.add(leaf1)
composite.add(leaf2)
composite.operation()
Coding Round
96. Visitor pattern

Visitor pattern for adding operations.

  • Visitor: Defines operations
  • Element: Accepts visitors
  • Benefits: Adding operations without modifying
  • Use case: Compilers, AST
kotlin
// Visitor pattern
interface Element {
    fun accept(visitor: Visitor)
}

class ElementA : Element {
    override fun accept(visitor: Visitor) {
        visitor.visit(this)
    }
}

class ElementB : Element {
    override fun accept(visitor: Visitor) {
        visitor.visit(this)
    }
}

interface Visitor {
    fun visit(element: ElementA)
    fun visit(element: ElementB)
}

class ConcreteVisitor : Visitor {
    override fun visit(element: ElementA) {
        println("Visiting ElementA")
    }
    override fun visit(element: ElementB) {
        println("Visiting ElementB")
    }
}

// Usage
val visitor = ConcreteVisitor()
val elementA = ElementA()
val elementB = ElementB()
elementA.accept(visitor)
elementB.accept(visitor)
Coding Round
97. Iterator pattern

Iterator pattern for sequential access.

  • Iterator: Traverses collection
  • Aggregate: Creates iterator
  • Benefits: Uniform traversal
  • Use case: Collection traversal
kotlin
// Iterator pattern
class Iterator<T>(private val collection: List<T>) {
    private var index = 0

    fun next(): T? {
        return if (hasNext()) collection[index++] else null
    }

    fun hasNext(): Boolean {
        return index < collection.size
    }
}

class CustomCollection<T> {
    private val items = mutableListOf<T>()

    fun add(item: T) {
        items.add(item)
    }

    fun getIterator(): Iterator<T> {
        return Iterator(items)
    }
}

// Usage
val collection = CustomCollection<String>()
collection.add("A")
collection.add("B")
collection.add("C")
val iterator = collection.getIterator()
while (iterator.hasNext()) {
    println(iterator.next())
}
Coding Round
98. Template Method pattern

Template Method for algorithm skeletons.

  • AbstractClass: Defines template
  • ConcreteClass: Implements steps
  • Benefits: Code reuse
  • Use case: Frameworks
kotlin
// Template Method pattern
abstract class AbstractClass {
    fun templateMethod() {
        step1()
        step2()
        step3()
    }

    open fun step1() {
        println("Step 1")
    }

    abstract fun step2()

    open fun step3() {
        println("Step 3")
    }
}

class ConcreteClass : AbstractClass() {
    override fun step2() {
        println("Concrete Step 2")
    }
}

// Usage
val concrete = ConcreteClass()
concrete.templateMethod()
Coding Round
99. Builder pattern

Builder pattern for constructing complex objects.

  • Builder: Constructs parts
  • Director: Orchestrates construction
  • Product: Constructed object
  • Benefits: Step-by-step construction
kotlin
// Builder pattern
class Product {
    private val parts = mutableListOf<String>()

    fun add(part: String) {
        parts.add(part)
    }

    fun listParts() {
        println(parts.joinToString(", "))
    }
}

class Builder {
    private val product = Product()

    fun reset() {
        product.parts.clear()
    }

    fun buildStepA() {
        product.add("Part A")
    }

    fun buildStepB() {
        product.add("Part B")
    }

    fun getResult(): Product {
        return product
    }
}

class Director(private val builder: Builder) {
    fun buildMinimal() {
        builder.buildStepA()
    }

    fun buildFull() {
        builder.buildStepA()
        builder.buildStepB()
    }
}

// Usage
val builder = Builder()
val director = Director(builder)
director.buildMinimal()
val product = builder.getResult()
product.listParts()
Coding Round
100. Prototype pattern

Prototype pattern for cloning objects.

  • Prototype: Cloneable object
  • Clone: Creates a copy
  • Benefits: Performance
  • Use case: Complex objects
kotlin
// Prototype pattern
data class Prototype(
    val name: String,
    val nested: MutableMap<String, Any>
) {
    fun clone(): Prototype {
        return copy(
            nested = nested.toMutableMap()
        )
    }

    fun deepClone(): Prototype {
        return copy(
            nested = nested.mapValues { (_, value) ->
                when (value) {
                    is MutableMap<*, *> -> (value as MutableMap<String, Any>).toMutableMap()
                    is List<*> -> value.toMutableList()
                    else -> value
                }
            }.toMutableMap()
        )
    }
}

// Usage
val original = Prototype("Original", mutableMapOf("value" to 42))
val copy = original.clone()
copy.name = "Copy"
copy.nested["value"] = 99
println(original.name) // Original
println(original.nested["value"]) // 42 (shallow copy)

val deepCopy = original.deepClone()
deepCopy.nested["value"] = 100
println(original.nested["value"]) // 42 (deep copy)