Kotlin Mastery Guide
Complete guide from basics to advanced Android development
The Complete Kotlin Mastery Guide
Master the modern programming language for Android, backend, and cross-platform development
Understanding Kotlin: The Modern Programming Language
Kotlin is a modern, statically-typed programming language developed by JetBrains that runs on the Java Virtual Machine (JVM). It was designed to be fully interoperable with Java while addressing many of Java's limitations and verbosity.
What makes Kotlin revolutionary is its concise syntax, null safety, functional programming features, and excellent tooling support. Google announced Kotlin as an official language for Android development in 2017, solidifying its position in the mobile development ecosystem.
Industry Impact: Kotlin has been adopted by major companies like Google, Netflix, Uber, Pinterest, and Trello. It's not just for Android - Kotlin is used for backend development, web frontend (Kotlin/JS), and native development (Kotlin/Native).
1. Kotlin Basics & Syntax
Getting Started: Your First Kotlin Program
// Hello World in Kotlin fun main() { println("Hello, World!") } // Variables and basic types fun variablesDemo() { // Immutable variables (read-only) val name = "John" // Type inferred as String val age: Int = 25 // Explicit type declaration val height = 1.75 // Double by default val isStudent = true // Boolean // Mutable variables var score = 0 score = 100 // Can be reassigned // Null safety var nullableName: String? = null // Nullable type nullableName = "Jane" // Can assign value // Safe calls val length = nullableName?.length // Returns null if nullableName is null // Elvis operator val safeLength = nullableName?.length ?: 0 // Default value if null // Type checking and smart casting val obj: Any = "Hello" if (obj is String) { println(obj.length) // Smart cast to String } // String templates println("Name: $name, Age: $age, Score: $score") println("Length of name: ${name.length}") } // Basic control flow fun controlFlowDemo() { val x = 10 val y = 20 // If-else as expression val max = if (x > y) x else y println("Maximum: $max") // When expression (enhanced switch) val grade = when { x >= 90 -> "A" x >= 80 -> "B" x >= 70 -> "C" else -> "F" } // Ranges for (i in 1..5) { print("$i ") // Output: 1 2 3 4 5 } for (i in 5 downTo 1 step 2) { print("$i ") // Output: 5 3 1 } // While loops var i = 0 while (i < 5) { println("i = $i") i++ } }
Data Types and Type System
// Basic data types fun dataTypesDemo() { // Numbers val byte: Byte = 127 val short: Short = 32767 val int: Int = 2147483647 val long: Long = 9223372036854775807L val float: Float = 3.14f val double: Double = 3.141592653589793 // Characters and strings val char: Char = 'A' val string: String = "Hello Kotlin" val rawString = """ This is a raw string It can span multiple lines Without escape characters """.trimIndent() // Arrays val numbers = arrayOf(1, 2, 3, 4, 5) val squares = Array(5) { i -> (i + 1) * (i + 1) } // Primitive type arrays (for performance) val intArray = intArrayOf(1, 2, 3) val charArray = charArrayOf('a', 'b', 'c') // Type conversions val stringNumber = "123" val intValue = stringNumber.toInt() val doubleValue = stringNumber.toDouble() // Any type (root of Kotlin class hierarchy) val anyValue: Any = "Can be any type" // Unit type (similar to void) fun doSomething(): Unit { // Unit return type can be omitted println("Doing something") } // Nothing type (function never returns) fun fail(message: String): Nothing { throw IllegalArgumentException(message) } } // Null safety in depth fun nullSafetyDemo() { // Regular types are non-nullable by default var name: String = "John" // Cannot be null // Nullable types var nullableName: String? = null // Safe call operator val length1 = nullableName?.length // Returns Int? // Elvis operator with throw val length2 = nullableName?.length ?: throw IllegalArgumentException("Name cannot be null") // Safe cast val obj: Any = "Hello" val stringObj = obj as? String // Returns null if cast fails // Not-null assertion (use carefully!) val forcedLength = nullableName!!.length // Throws NPE if null // let function for null checks nullableName?.let { println("Name is $it and length is ${it.length}") } // also function for additional operations val result = "Hello".also { println("Original value: $it") } } // Strings and text processing fun stringOperations() { val text = "Kotlin Programming" // Basic operations println(text.length) // 18 println(text.uppercase()) // "KOTLIN PROGRAMMING" println(text.lowercase()) // "kotlin programming" println(text.substring(0, 6)) // "Kotlin" // String templates val language = "Kotlin" val version = 1.9 println("Welcome to $language version $version") println("2 + 2 = ${2 + 2}") // Multiline strings val html = """ <html> <body> <h1>Welcome to Kotlin</h1> </body> </html> """.trimIndent() // String builders val builder = StringBuilder() builder.append("Hello") .append(" ") .append("Kotlin") val result = builder.toString() // Regular expressions val regex = Regex("""d+""") // Raw string for regex val hasDigits = regex.containsMatchIn("abc123") val digits = regex.findAll("abc123def456").map { it.value }.toList() }
Control Flow and Expressions
// If expressions fun ifExpressions(x: Int, y: Int) { // Traditional if-else if (x > y) { println("x is greater") } else { println("y is greater or equal") } // If as expression val max = if (x > y) x else y val result = if (x > y) { "x is greater" } else if (x < y) { "y is greater" } else { "x and y are equal" } } // When expression (powerful switch replacement) fun whenExpressions(value: Any) { // When with argument when (value) { 1 -> println("One") 2 -> println("Two") 3, 4 -> println("Three or Four") // Multiple values in 5..10 -> println("Between 5 and 10") // Range check is String -> println("It's a string with length ${value.length}") // Type check else -> println("Something else") } // When without argument (like if-else chain) val score = 85 val grade = when { score >= 90 -> "A" score >= 80 -> "B" score >= 70 -> "C" score >= 60 -> "D" else -> "F" } // When with function return fun describe(obj: Any): String = when (obj) { 1 -> "One" "Hello" -> "Greeting" is Long -> "Long" !is String -> "Not a string" else -> "Unknown" } } // Loops and iterations fun loopExamples() { // For loops with ranges for (i in 1..5) print("$i ") // 1 2 3 4 5 println() for (i in 1 until 5) print("$i ") // 1 2 3 4 println() for (i in 5 downTo 1) print("$i ") // 5 4 3 2 1 println() for (i in 1..10 step 2) print("$i ") // 1 3 5 7 9 println() // Iterating over arrays and lists val names = listOf("Alice", "Bob", "Charlie") for (name in names) { println(name) } for (index in names.indices) { println("Name at $index is ${names[index]}") } for ((index, name) in names.withIndex()) { println("Name at $index is $name") } // While and do-while var x = 5 while (x > 0) { println(x) x-- } var y = 0 do { println("y = $y") y++ } while (y < 5) // Break and continue with labels outer@ for (i in 1..3) { inner@ for (j in 1..3) { if (i == 2 && j == 2) break@outer println("i=$i, j=$j") } } } // Exception handling fun exceptionHandling() { // Try-catch as expression val number = try { "123".toInt() } catch (e: NumberFormatException) { 0 // Default value } // Multiple catch blocks try { val result = 10 / 0 } catch (e: ArithmeticException) { println("Arithmetic error: ${e.message}") } catch (e: Exception) { println("General error: ${e.message}") } finally { println("This always executes") } // Custom exceptions class ValidationException(message: String) : Exception(message) fun validateAge(age: Int) { if (age < 0) { throw ValidationException("Age cannot be negative") } } }
Basic I/O and Console Applications
// Basic input/output operations fun ioOperations() { // Output print("Hello") // No newline println("World") // With newline // String formatting val name = "Alice" val age = 30 println("Name: %s, Age: %d".format(name, age)) println("Name: $name, Age: $age") // String templates // Input from console print("Enter your name: ") val userName = readLine() // Returns String? print("Enter your age: ") val userAge = readLine()?.toIntOrNull() ?: 0 println("Hello $userName, you are $userAge years old!") // File operations (basic) import java.io.File // Reading from file val file = File("data.txt") if (file.exists()) { val content = file.readText() println("File content: $content") // Reading line by line file.forEachLine { line -> println("Line: $line") } } // Writing to file val outputFile = File("output.txt") outputFile.writeText("Hello Kotlin!\n") outputFile.appendText("Another line\n") } // Simple console application example fun calculator() { while (true) { println("=== Simple Calculator ===") println("1. Add") println("2. Subtract") println("3. Multiply") println("4. Divide") println("5. Exit") print("Choose operation: ") val choice = readLine()?.toIntOrNull() if (choice == 5) { println("Goodbye!") break } if (choice !in 1..4) { println("Invalid choice!") continue } print("Enter first number: ") val num1 = readLine()?.toDoubleOrNull() print("Enter second number: ") val num2 = readLine()?.toDoubleOrNull() if (num1 == null || num2 == null) { println("Invalid numbers!") continue } val result = when (choice) { 1 -> num1 + num2 2 -> num1 - num2 3 -> num1 * num2 4 -> if (num2 != 0.0) num1 / num2 else "Cannot divide by zero" else -> "Invalid operation" } println("Result: $result\n") } } // Number guessing game fun guessingGame() { val randomNumber = (1..100).random() var attempts = 0 var guessed = false println("Welcome to the Number Guessing Game!") println("I'm thinking of a number between 1 and 100.") while (!guessed && attempts < 10) { print("Enter your guess: ") val guess = readLine()?.toIntOrNull() if (guess == null) { println("Please enter a valid number!") continue } attempts++ when { guess < randomNumber -> println("Too low!") guess > randomNumber -> println("Too high!") else -> { println("Congratulations! You guessed the number in $attempts attempts!") guessed = true } } } if (!guessed) { println("Game over! The number was $randomNumber") } }
2. Functions & Object-Oriented Programming
Functions and Functional Programming
// Basic function syntax fun greet(name: String): String { return "Hello, $name!" } // Single-expression functions fun square(x: Int): Int = x * x // Function with default parameters fun createMessage( name: String, title: String = "Mr./Ms.", punctuation: String = "!" ): String { return "Hello, $title $name$punctuation" } // Function with varargs fun sum(vararg numbers: Int): Int { return numbers.sum() } // Higher-order functions fun calculate(x: Int, y: Int, operation: (Int, Int) -> Int): Int { return operation(x, y) } // Lambda expressions val multiply = { x: Int, y: Int -> x * y } // Function types typealias Arithmetic = (Int, Int) -> Int // Extension functions fun String.addExcitement(): String = this + "!!!" // Infix functions infix fun Int.times(str: String) = str.repeat(this) // Function examples in action fun functionExamples() { // Basic function calls println(greet("Alice")) // Hello, Alice! println(square(5)) // 25 // Default parameters println(createMessage("Bob")) // Hello, Mr./Ms. Bob! println(createMessage("Charlie", "Dr.")) // Hello, Dr. Charlie! println(createMessage("Diana", punctuation = "?")) // Hello, Mr./Ms. Diana? // Varargs println(sum(1, 2, 3, 4, 5)) // 15 // Higher-order functions val result1 = calculate(10, 5) { a, b -> a + b } // 15 val result2 = calculate(10, 5, multiply) // 50 // Extension function println("Hello".addExcitement()) // Hello!!! // Infix function println(3 times "Hi ") // Hi Hi Hi } // Tail recursion for optimized recursion tailrec fun factorial(n: Int, accumulator: Int = 1): Int { return if (n <= 1) accumulator else factorial(n - 1, n * accumulator) } // Inline functions for performance inline fun measureTime(block: () -> Unit): Long { val start = System.currentTimeMillis() block() return System.currentTimeMillis() - start } // Function scope and visibility class MathUtils { // Public by default fun add(a: Int, b: Int) = a + b // Private function private fun validateInput(x: Int) = x > 0 // Protected function protected fun internalCalc() = 42 // Local function fun complexCalculation(x: Int, y: Int): Int { fun validate(num: Int): Boolean = num > 0 return if (validate(x) && validate(y)) x + y else 0 } }
Classes and Objects
// Basic class definition class Person( val name: String, // Read-only property var age: Int, // Mutable property val city: String = "Unknown" // Default value ) { // Secondary constructor constructor(name: String) : this(name, 0) // Properties with custom getters/setters var email: String = "" set(value) { field = value.lowercase() } val isAdult: Boolean get() = age >= 18 // Methods fun introduce() { println("Hello, I'm $name, $age years old from $city") } // Companion object (static members) companion object { const val SPECIES = "Homo sapiens" fun createBaby(name: String): Person { return Person(name, 0) } } } // Data classes (automatically generated methods) data class User( val id: Int, val username: String, val email: String, val createdAt: String = "" ) { // Custom methods can be added fun displayInfo() = "$username ($email)" } // Enum classes enum class Color(val rgb: Int) { RED(0xFF0000), GREEN(0x00FF00), BLUE(0x0000FF); fun containsRed() = (this.rgb and 0xFF0000) != 0 } // Sealed classes for restricted hierarchies sealed class Result<out T> { data class Success<T>(val data: T) : Result<T>() data class Error(val message: String) : Result<Nothing>() object Loading : Result<Nothing>() } // Object expressions and declarations interface ClickListener { fun onClick() } // Object expression (anonymous class) val clickListener = object : ClickListener { override fun onClick() { println("Button clicked!") } } // Object declaration (singleton) object DatabaseManager { private var connectionCount = 0 fun connect() { connectionCount++ println("Connected to database. Total connections: $connectionCount") } fun disconnect() { if (connectionCount > 0) connectionCount-- println("Disconnected from database. Remaining connections: $connectionCount") } } // Class usage examples fun classExamples() { // Creating objects val person1 = Person("Alice", 25, "New York") val person2 = Person("Bob") // Using secondary constructor // Accessing properties println(person1.name) // Alice person1.age = 26 // Modifying property person1.introduce() // Hello, I'm Alice, 26 years old from New York // Data class usage val user1 = User(1, "john_doe", "john@example.com") val user2 = user1.copy(username = "john_updated") // Copy with changes println(user1) // Automatically generated toString() println(user1 == user2) // Automatically generated equals() // Enum usage val color = Color.RED println(color.name) // RED println(color.ordinal) // 0 println(color.containsRed()) // true // Sealed class usage fun handleResult(result: Result<String>) { when (result) { is Result.Success -> println("Data: ${result.data}") is Result.Error -> println("Error: ${result.message}") Result.Loading -> println("Loading...") } } // Singleton usage DatabaseManager.connect() DatabaseManager.connect() DatabaseManager.disconnect() }
Inheritance and Interfaces
// Open classes (Kotlin classes are final by default) open class Animal(val name: String, val age: Int) { open fun makeSound() { println("Some generic animal sound") } fun sleep() { println("$name is sleeping") } } // Inheritance class Dog(name: String, age: Int, val breed: String) : Animal(name, age) { override fun makeSound() { println("Woof! Woof!") } fun fetch() { println("$name is fetching the ball") } } // Abstract classes abstract class Shape(val name: String) { abstract fun area(): Double abstract fun perimeter(): Double fun displayInfo() { println("Shape: $name, Area: ${area()}, Perimeter: ${perimeter()}") } } class Circle(val radius: Double) : Shape("Circle") { override fun area(): Double = Math.PI * radius * radius override fun perimeter(): Double = 2 * Math.PI * radius } class Rectangle(val width: Double, val height: Double) : Shape("Rectangle") { override fun area(): Double = width * height override fun perimeter(): Double = 2 * (width + height) } // Interfaces with default implementations interface Drawable { fun draw() fun erase() { println("Erasing the drawing") } } interface Colorable { var color: String fun fillColor() { println("Filling with $color") } } // Multiple interface implementation class Square(val side: Double) : Shape("Square"), Drawable, Colorable { override var color: String = "Black" override fun area(): Double = side * side override fun perimeter(): Double = 4 * side override fun draw() { println("Drawing a square with side $side") } // Override default interface method override fun fillColor() { println("Filling square with $color") } } // Interface delegation interface Repository { fun getData(): String } class DatabaseRepository : Repository { override fun getData(): String = "Data from database" } class CachedRepository(private val repository: Repository) : Repository by repository { private var cachedData: String? = null override fun getData(): String { if (cachedData == null) { cachedData = repository.getData() println("Data cached") } return cachedData!! } } // Inheritance examples fun inheritanceExamples() { // Basic inheritance val dog = Dog("Buddy", 3, "Golden Retriever") dog.makeSound() // Woof! Woof! dog.sleep() // Buddy is sleeping dog.fetch() // Buddy is fetching the ball // Polymorphism val animal: Animal = Dog("Max", 2, "Labrador") animal.makeSound() // Woof! Woof! (dynamic dispatch) // Abstract class usage val circle = Circle(5.0) val rectangle = Rectangle(4.0, 6.0) circle.displayInfo() // Shape: Circle, Area: 78.54, Perimeter: 31.42 rectangle.displayInfo() // Shape: Rectangle, Area: 24.0, Perimeter: 20.0 // Multiple interface implementation val square = Square(4.0) square.color = "Blue" square.draw() // Drawing a square with side 4.0 square.fillColor() // Filling square with Blue square.displayInfo() // Shape: Square, Area: 16.0, Perimeter: 16.0 // Interface delegation val repository = CachedRepository(DatabaseRepository()) println(repository.getData()) // Data from database (with caching) println(repository.getData()) // Data from database (from cache) }
Properties and Delegates
// Property delegation examples import kotlin.properties.Delegates import kotlin.reflect.KProperty // Custom delegate class RangeDelegate(private val min: Int, private val max: Int) { private var value: Int = min operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { return value } operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: Int) { if (newValue in min..max) { value = newValue } else { throw IllegalArgumentException("Value must be between $min and $max") } } } class Product { // Lazy initialization val expensiveValue: String by lazy { println("Computing expensive value...") "Expensive Result" } // Observable property var price: Double by Delegates.observable(0.0) { prop, old, new -> println("Price changed from $old to $new") } // Vetoable property (can reject changes) var quantity: Int by Delegates.vetoable(0) { prop, old, new -> new >= 0 // Only allow non-negative quantities } // Custom delegate var rating: Int by RangeDelegate(1, 5) // Late initialization lateinit var name: String fun initializeName() { name = "Product Name" } fun checkInitialization() { if (::name.isInitialized) { println("Name is initialized: $name") } else { println("Name is not initialized") } } } // Built-in delegates examples fun delegateExamples() { val product = Product() // Lazy delegate println(product.expensiveValue) // Computing expensive value... Expensive Result println(product.expensiveValue) // Expensive Result (cached) // Observable delegate product.price = 19.99 // Price changed from 0.0 to 19.99 product.price = 15.99 // Price changed from 19.99 to 15.99 // Vetoable delegate product.quantity = 10 // Allowed println("Quantity: ${product.quantity}") // 10 product.quantity = -5 // Rejected (remains 10) println("Quantity: ${product.quantity}") // 10 // Custom delegate product.rating = 4 // Allowed println("Rating: ${product.rating}") // 4 try { product.rating = 6 // Throws IllegalArgumentException } catch (e: IllegalArgumentException) { println("Error: ${e.message}") } // Lateinit product.checkInitialization() // Name is not initialized product.initializeName() product.checkInitialization() // Name is initialized: Product Name } // Map delegation class Config(map: Map<String, Any?>) { val serverUrl: String by map val port: Int by map val timeout: Long by map } fun mapDelegationExample() { val configMap = mapOf( "serverUrl" to "https://api.example.com", "port" to 8080, "timeout" to 30000L ) val config = Config(configMap) println("Server: ${config.serverUrl}") // https://api.example.com println("Port: ${config.port}") // 8080 println("Timeout: ${config.timeout}") // 30000 } // Property delegation for caching class CacheDelegate<T> { private var value: T? = null private var computed = false operator fun getValue(thisRef: Any?, property: KProperty<*>): T { if (!computed) { throw IllegalStateException("Value not computed yet") } return value!! } operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T) { value = newValue computed = true } } class DataProcessor { var processedData: String by CacheDelegate() fun processData(data: String) { // Simulate expensive processing processedData = "PROCESSED: $data" } }
3. Collections & Functional Programming
Collections Framework
// Collection types and creation fun collectionTypes() { // Lists val immutableList = listOf("apple", "banana", "orange") val mutableList = mutableListOf("red", "green", "blue") // Sets val immutableSet = setOf(1, 2, 3, 2, 1) // Duplicates removed: [1, 2, 3] val mutableSet = mutableSetOf("a", "b", "c") // Maps val immutableMap = mapOf( "name" to "John", "age" to 30, "city" to "New York" ) val mutableMap = mutableMapOf<String, Any>() // Arrays vs Lists val array = arrayOf(1, 2, 3) // Array - fixed size, primitive-friendly val list = listOf(1, 2, 3) // List - read-only interface val mutable = mutableListOf(1, 2, 3) // MutableList - can modify // Empty collections val emptyList = emptyList<String>() val emptySet = emptySet<Int>() val emptyMap = emptyMap<String, Any>() // Collection builders val builtList = buildList { add("first") addAll(listOf("second", "third")) if (true) add("conditional") } } // Basic collection operations fun basicOperations() { val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) val names = listOf("Alice", "Bob", "Charlie", "Diana", "Eve") // Filtering val evenNumbers = numbers.filter { it % 2 == 0 } // [2, 4, 6, 8, 10] val longNames = names.filter { it.length > 4 } // [Alice, Charlie] val notEve = names.filterNot { it == "Eve" } // All except Eve val (even, odd) = numbers.partition { it % 2 == 0 } // Pair of lists // Transformation val squares = numbers.map { it * it } // [1, 4, 9, ...] val nameLengths = names.map { it.length } // [5, 3, 7, 5, 3] val indexed = names.mapIndexed { index, name -> "$index: $name" } // Sorting val sortedNames = names.sorted() // Alphabetical val sortedByLength = names.sortedBy { it.length } // By length val descending = numbers.sortedDescending() // [10, 9, 8, ...] // Aggregation val sum = numbers.sum() // 55 val average = numbers.average() // 5.5 val max = numbers.maxOrNull() // 10 val min = numbers.minOrNull() // 1 val count = numbers.count { it > 5 } // 5 // Finding elements val firstEven = numbers.first { it % 2 == 0 } // 2 val lastOdd = numbers.last { it % 2 == 1 } // 9 val firstOrNull = numbers.firstOrNull { it > 100 } // null // Grouping val groupedByLength = names.groupBy { it.length } // Map<Int, List<String>> val grouped = numbers.groupBy { if (it % 2 == 0) "even" else "odd" } } // Advanced collection operations fun advancedOperations() { val numbers = (1..10).toList() val names = listOf("Alice", "Bob", "Charlie", "Diana", "Eve", "Alice") // Distinct operations val distinctNames = names.distinct() // Remove duplicates val distinctByLength = names.distinctBy { it.length } // Distinct by property // Flattening val nestedList = listOf(listOf(1, 2), listOf(3, 4), listOf(5, 6)) val flattened = nestedList.flatten() // [1, 2, 3, 4, 5, 6] // Zipping val list1 = listOf("A", "B", "C") val list2 = listOf(1, 2, 3) val zipped = list1.zip(list2) // [("A", 1), ("B", 2), ("C", 3)] val zippedWithTransform = list1.zip(list2) { a, b -> "$a$b" } // ["A1", "B2", "C3"] // Windowing val windows = numbers.windowed(3) // [[1,2,3], [2,3,4], ...] val sliding = numbers.windowed(3, step = 2) // [[1,2,3], [3,4,5], ...] // Chunking val chunks = numbers.chunked(3) // [[1,2,3], [4,5,6], [7,8,9], [10]] // String operations on collections val joined = names.joinToString() // "Alice, Bob, Charlie, ..." val joinedWithPrefix = names.joinToString(prefix = "[", postfix = "]") val joinedWithTransform = names.joinToString { it.uppercase() } } // Functional programming with collections fun functionalProgramming() { data class Person(val name: String, val age: Int, val city: String) val people = listOf( Person("Alice", 25, "New York"), Person("Bob", 30, "London"), Person("Charlie", 22, "New York"), Person("Diana", 28, "Paris"), Person("Eve", 35, "London") ) // Complex data processing pipeline val result = people .filter { it.age > 25 } // People over 25 .groupBy { it.city } // Group by city .mapValues { (city, peopleInCity) -> // Transform each group peopleInCity .sortedByDescending { it.age } // Sort by age descending .map { it.name } // Extract names .take(2) // Take top 2 } .filter { it.value.size >= 2 } // Cities with at least 2 people println(result) // {London=[Eve, Bob], New York=[Charlie]} // Fold and reduce val numbers = listOf(1, 2, 3, 4, 5) val sum = numbers.reduce { acc, num -> acc + num } // 15 val product = numbers.fold(1) { acc, num -> acc * num } // 120 // Running fold (intermediate results) val runningSum = numbers.runningReduce { acc, num -> acc + num } // [1, 3, 6, 10, 15] val runningFold = numbers.scan(0) { acc, num -> acc + num } // [0, 1, 3, 6, 10, 15] // Lazy sequences for large datasets val largeSequence = generateSequence(1) { it + 1 } .take(1000000) .filter { it % 2 == 0 } .map { it * 2 } .toList() // Operations are executed lazily // Collection transformation to other types val nameMap = people.associateBy { it.name } // Map<String, Person> val ageMap = people.associate { it.name to it.age } // Map<String, Int> val cityGroups = people.groupBy { it.city } // Map<String, List<Person>> // Set operations val set1 = setOf(1, 2, 3, 4) val set2 = setOf(3, 4, 5, 6) val union = set1.union(set2) // [1, 2, 3, 4, 5, 6] val intersect = set1.intersect(set2) // [3, 4] val subtract = set1.subtract(set2) // [1, 2] }
Functional Programming Concepts
// Higher-order functions in depth fun higherOrderFunctions() { // Function types val stringMapper: (String) -> Int = { it.length } val predicate: (Int) -> Boolean = { it > 0 } val transformer: (String, Int) -> String = { str, num -> str.repeat(num) } // Functions returning functions fun createMultiplier(factor: Int): (Int) -> Int { return { number -> number * factor } } val double = createMultiplier(2) val triple = createMultiplier(3) println(double(5)) // 10 println(triple(5)) // 15 // Functions taking functions as parameters fun <T, R> processItems( items: List<T>, transform: (T) -> R, filter: (T) -> Boolean = { true }, action: (R) -> Unit ) { items.filter(filter) .map(transform) .forEach(action) } val numbers = listOf(1, 2, 3, 4, 5) processItems( items = numbers, transform = { it * it }, filter = { it % 2 == 0 }, action = { println("Result: $it") } ) // Output: Result: 4, Result: 16 } // Lambda expressions and closures fun lambdaExamples() { // Basic lambda syntax val adder = { x: Int, y: Int -> x + y } val greeter = { name: String -> "Hello, $name!" } // Lambda with receiver val stringBuilder: StringBuilder.() -> Unit = { append("Hello") append(" ") append("World") } val result = StringBuilder().apply(stringBuilder).toString() println(result) // Hello World // Capturing variables (closures) var counter = 0 val incrementer = { counter++ println("Counter: $counter") } incrementer() // Counter: 1 incrementer() // Counter: 2 // Function references fun isEven(number: Int) = number % 2 == 0 fun square(number: Int) = number * number val numbers = listOf(1, 2, 3, 4, 5) val evenSquares = numbers.filter(::isEven) .map(::square) // Method references val strings = listOf("apple", "banana", "cherry") val uppercaseStrings = strings.map(String::uppercase) } // Function composition and currying fun advancedFunctional() { // Function composition fun <A, B, C> compose(f: (B) -> C, g: (A) -> B): (A) -> C = { x -> f(g(x)) } val length = { s: String -> s.length } val isEven = { n: Int -> n % 2 == 0 } val isStringLengthEven = compose(isEven, length) println(isStringLengthEven("Hello")) // false (length 5) println(isStringLengthEven("Hi")) // true (length 2) // Currying fun <A, B, C> curry(f: (A, B) -> C): (A) -> (B) -> C = { a -> { b -> f(a, b) } } val add = { a: Int, b: Int -> a + b } val curriedAdd = curry(add) val add5 = curriedAdd(5) println(add5(3)) // 8 println(add5(10)) // 15 // Partial application fun multiply(a: Int, b: Int, c: Int) = a * b * c val multiplyBy2 = { b: Int, c: Int -> multiply(2, b, c) } val multiplyBy2And3 = { c: Int -> multiply(2, 3, c) } // Memoization for expensive functions fun <A, R> memoize(f: (A) -> R): (A) -> R { val cache = mutableMapOf<A, R>() return { a -> cache.getOrPut(a) { f(a) } } } val expensiveFunction = { n: Int -> println("Computing for $n") (1..n).reduce { acc, i -> acc * i } } val memoizedFunction = memoize(expensiveFunction) println(memoizedFunction(5)) // Computing for 5, then 120 println(memoizedFunction(5)) // 120 (from cache) } // Functional data structures and patterns fun functionalPatterns() { // Option pattern with nullable types fun findUser(id: Int): String? = when (id) { 1 -> "Alice" 2 -> "Bob" else -> null } fun getEmail(user: String): String? = when (user) { "Alice" -> "alice@example.com" "Bob" -> "bob@example.com" else -> null } // Chaining nullable operations val email = findUser(1)?.let { user -> getEmail(user) } println(email) // alice@example.com // Either pattern with sealed classes sealed class Either<out L, out R> { data class Left<out L>(val value: L) : Either<L, Nothing>() data class Right<out R>(val value: R) : Either<Nothing, R>() } fun parseNumber(s: String): Either<String, Int> = try { Either.Right(s.toInt()) } catch (e: NumberFormatException) { Either.Left("Invalid number: $s") } // Using Either when (val result = parseNumber("123")) { is Either.Right -> println("Parsed number: ${result.value}") is Either.Left -> println("Error: ${result.value}") } // Validation with multiple errors data class ValidationError(val field: String, val message: String) fun validateName(name: String): List<ValidationError> = buildList { if (name.isBlank()) add(ValidationError("name", "Name cannot be blank")) if (name.length < 2) add(ValidationError("name", "Name too short")) } fun validateAge(age: Int): List<ValidationError> = buildList { if (age < 0) add(ValidationError("age", "Age cannot be negative")) if (age > 150) add(ValidationError("age", "Age seems invalid")) } }
Collection Performance and Best Practices
// Performance characteristics fun collectionPerformance() { // List performance val list = listOf(1, 2, 3, 4, 5) // ArrayList - O(1) for get/set, O(n) for add/remove at beginning val arrayList = arrayListOf(1, 2, 3, 4, 5) arrayList.add(6) // O(1) amortized arrayList.add(0, 0) // O(n) arrayList[2] // O(1) // LinkedList - O(n) for get/set, O(1) for add/remove at ends val linkedList = LinkedList(list) linkedList.addFirst(0) // O(1) linkedList.get(2) // O(n) // Set performance val hashSet = hashSetOf(1, 2, 3, 4, 5) // O(1) for contains/add/remove val sortedSet = sortedSetOf(1, 2, 3) // O(log n) for operations // Map performance val hashMap = hashMapOf("a" to 1, "b" to 2) // O(1) for get/put val sortedMap = sortedMapOf("a" to 1, "b" to 2) // O(log n) for operations // Choosing the right collection fun chooseCollection(useCase: String) { when (useCase) { "frequent access by index" -> ArrayList() "frequent add/remove at ends" -> LinkedList() "unique elements with fast lookup" -> HashSet() "sorted unique elements" -> TreeSet() "key-value pairs with fast lookup" -> HashMap() "sorted key-value pairs" -> TreeMap() else -> List // Default to immutable list } } } // Memory efficiency with sequences fun sequenceVsCollection() { val largeList = (1..1_000_000).toList() // Eager evaluation with lists (creates intermediate collections) val result1 = largeList .filter { it % 2 == 0 } // Creates new list of ~500,000 elements .map { it * it } // Creates another new list .take(10) // Takes first 10 .toList() // Lazy evaluation with sequences (no intermediate collections) val result2 = largeList .asSequence() // Convert to sequence .filter { it % 2 == 0 } // Lazy operation .map { it * it } // Lazy operation .take(10) // Only processes first 10 matching elements .toList() // Terminal operation // Sequence generation val infiniteSequence = generateSequence(1) { it + 1 } val finiteSequence = generateSequence(1) { if (it < 100) it + 1 else null } val fibonacci = sequence { var a = 0 var b = 1 while (true) { yield(a) val next = a + b a = b b = next } } val first10Fibonacci = fibonacci.take(10).toList() println(first10Fibonacci) // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] } // Collection best practices fun collectionBestPractices() { // Prefer immutable collections val immutableList = listOf(1, 2, 3) // Read-only val immutableMap = mapOf("a" to 1, "b" to 2) // Use mutable collections only when needed val mutableList = mutableListOf(1, 2, 3) mutableList.add(4) // Only if you need to modify // Use collection builders for complex initialization val complexList = buildList { add("header") addAll(listOf("item1", "item2")) if (condition) add("conditional") } // Use specific collection types for performance val frequentLookupSet = hashSetOf("a", "b", "c") val sortedValues = sortedSetOf(3, 1, 2) // [1, 2, 3] // Avoid unnecessary collection copies val original = listOf(1, 2, 3) // Good - uses same collection val processed = original.map { it * 2 } // Bad - unnecessary copy val unnecessaryCopy = original.toMutableList().also { it.add(4) } // Use sequences for large datasets or chained operations val largeData = (1..1_000_000).toList() val optimized = largeData.asSequence() .filter { it % 2 == 0 } .map { it * it } .take(1000) .toList() // Collection utility functions val numbers = listOf(1, 2, 3, 4, 5) // Safe operations val firstOrNull = numbers.firstOrNull { it > 10 } // null instead of exception val elementAtOrNull = numbers.elementAtOrNull(10) // null instead of exception // Default values val firstOrDefault = numbers.firstOrNull() ?: -1 val maxOrZero = numbers.maxOrNull() ?: 0 // Collection transformations val associateBy = numbers.associateBy { "key_$it" } // Map<String, Int> val groupBy = numbers.groupBy { it % 2 } // Map<Int, List<Int>> // Collection checks val allPositive = numbers.all { it > 0 } // true val anyEven = numbers.any { it % 2 == 0 } // true val noneNegative = numbers.none { it < 0 } // true }
4. Coroutines & Asynchronous Programming
Introduction to Coroutines
// Basic coroutine setup (requires kotlinx-coroutines-core dependency) import kotlinx.coroutines.* fun main() { // Launching first coroutine println("Main program starts: ${Thread.currentThread().name}") // GlobalScope (use carefully - coroutine lives as long as the application) GlobalScope.launch { println("Coroutine starts: ${Thread.currentThread().name}") delay(1000) // Suspends coroutine without blocking the thread println("Coroutine ends: ${Thread.currentThread().name}") } // Keep main thread alive to see coroutine output Thread.sleep(2000) println("Main program ends: ${Thread.currentThread().name}") } // Structured concurrency with runBlocking fun structuredConcurrency() { // runBlocking blocks the current thread until all coroutines inside complete runBlocking { println("Main coroutine starts: ${Thread.currentThread().name}") // Launch child coroutine launch { println("Child coroutine starts: ${Thread.currentThread().name}") delay(1000) println("Child coroutine ends: ${Thread.currentThread().name}") } println("Main coroutine continues: ${Thread.currentThread().name}") // runBlocking waits for all launched coroutines to complete } } // Coroutine builders: launch vs async fun coroutineBuilders() = runBlocking { // launch - fire and forget, returns Job val job = launch { delay(1000) println("World!") } println("Hello,") job.join() // Wait for the job to complete // async - returns Deferred<T> (like Future/Promise) val deferred: Deferred<Int> = async { delay(1000) 42 // Returns a value } println("Waiting for result...") val result = deferred.await() // Get the result println("Result: $result") // Parallel decomposition val time = measureTimeMillis { val one = async { doSomethingUsefulOne() } val two = async { doSomethingUsefulTwo() } println("The answer is ${one.await() + two.await()}") } println("Completed in $time ms") } suspend fun doSomethingUsefulOne(): Int { delay(1000) return 13 } suspend fun doSomethingUsefulTwo(): Int { delay(1000) return 29 } // Coroutine context and dispatchers fun coroutineContext() = runBlocking { // Different dispatchers for different use cases // Default - CPU-intensive work launch(Dispatchers.Default) { println("Default: ${Thread.currentThread().name}") } // IO - network or disk operations launch(Dispatchers.IO) { println("IO: ${Thread.currentThread().name}") } // Main - UI updates (Android) // launch(Dispatchers.Main) { // updateUI() // } // Unconfined - not confined to any specific thread launch(Dispatchers.Unconfined) { println("Unconfined: ${Thread.currentThread().name}") } // Custom thread pool val customDispatcher = newSingleThreadContext("MyThread") launch(customDispatcher) { println("Custom: ${Thread.currentThread().name}") } customDispatcher.close() }
Advanced Coroutine Patterns
// Coroutine scope and structured concurrency class MyActivity { private val scope = CoroutineScope(Dispatchers.Main + Job()) fun doWork() { scope.launch { try { val data = fetchData() updateUI(data) } catch (e: Exception) { showError(e) } } } fun onDestroy() { scope.cancel() // Cancel all coroutines when activity is destroyed } private suspend fun fetchData(): String { return withContext(Dispatchers.IO) { // Simulate network call delay(1000) "Data from server" } } private fun updateUI(data: String) { println("Updating UI with: $data") } private fun showError(e: Exception) { println("Error: ${e.message}") } } // Error handling in coroutines fun errorHandling() = runBlocking { // Try-catch in coroutine val job = launch { try { riskyOperation() } catch (e: Exception) { println("Caught exception: ${e.message}") } } job.join() // Using CoroutineExceptionHandler val exceptionHandler = CoroutineExceptionHandler { _, exception -> println("CoroutineExceptionHandler caught: ${exception.message}") } val supervisedJob = SupervisorJob() val scope = CoroutineScope(Dispatchers.Default + supervisedJob + exceptionHandler) scope.launch { throw RuntimeException("Failed coroutine") } delay(1000) scope.cancel() } suspend fun riskyOperation() { delay(500) throw RuntimeException("Something went wrong!") } // Coroutine patterns for common use cases fun commonPatterns() = runBlocking { // Timeout handling val result = withTimeoutOrNull(1300) { repeat(1000) { i -> println("I'm sleeping $i ...") delay(500) } "Done" // This line won't be reached due to timeout } println("Result: $result") // null due to timeout // Retry mechanism suspend fun <T> retry( times: Int = 3, delay: Long = 1000, block: suspend () -> T ): T { repeat(times - 1) { attempt -> try { return block() } catch (e: Exception) { println("Attempt $attempt failed: ${e.message}") delay(delay * (attempt + 1)) // Exponential backoff } } return block() // Last attempt } val data = retry(times = 3) { // Simulate flaky operation if (Math.random() < 0.7) throw RuntimeException("Temporary failure") "Success data" } println("Final result: $data") // Parallel processing with error handling val deferredList = listOf( async { processItem(1) }, async { processItem(2) }, async { processItem(3) } ) val results = deferredList.awaitAll() println("All results: $results") } suspend fun processItem(item: Int): String { delay(1000) return "Processed $item" } // Flows for reactive streams fun flowExamples() = runBlocking { // Basic flow fun simpleFlow() = flow { for (i in 1..3) { delay(100) emit(i) // Emit value to the flow } } // Collect flow values simpleFlow().collect { value -> println("Received: $value") } // Flow operators fun numberFlow() = flow { for (i in 1..5) { emit(i) } } numberFlow() .filter { it % 2 == 0 } .map { it * it } .collect { println("Processed: $it") } // Exception handling in flows fun flowWithError() = flow { for (i in 1..3) { if (i == 2) throw RuntimeException("Error on $i") emit(i) } } flowWithError() .catch { e -> emit(-1) } // Handle exception and emit fallback .collect { println("Value: $it") } // StateFlow and SharedFlow for state management val stateFlow = MutableStateFlow(0) // Collect state changes val job = launch { stateFlow.collect { value -> println("State changed to: $value") } } stateFlow.value = 1 stateFlow.value = 2 job.cancel() }
Channels and Actor Patterns
// Basic channel operations fun channelBasics() = runBlocking { // Create a channel val channel = Channel<Int>() // Producer coroutine launch { for (x in 1..5) { channel.send(x * x) // Send data to channel delay(100) } channel.close() // Close channel when done } // Consumer coroutine launch { for (y in channel) { // Iterate over channel values println("Received: $y") } println("Channel closed") } } // Different channel types fun channelTypes() = runBlocking { // Rendezvous channel (default) - no buffer val rendezvousChannel = Channel<Int>() // Buffered channel val bufferedChannel = Channel<Int>(10) // Buffer of 10 // Conflated channel - only keeps the latest value val conflatedChannel = Channel<Int>(Channel.CONFLATED) // Unlimited channel - theoretically unlimited buffer val unlimitedChannel = Channel<Int>(Channel.UNLIMITED) // Producer context val producer = launch { repeat(5) { i -> println("Sending: $i") bufferedChannel.send(i) } bufferedChannel.close() } // Consumer context val consumer = launch { for (value in bufferedChannel) { println("Received: $value") delay(200) // Slower than producer } } producer.join() consumer.join() } // Actor pattern for shared mutable state fun actorPattern() = runBlocking { // Message types for actor sealed class CounterMessage { object Increment : CounterMessage() class GetValue(val response: CompletableDeferred<Int>) : CounterMessage() } // Actor coroutine fun CoroutineScope.counterActor() = actor<CounterMessage> { var counter = 0 for (msg in channel) { when (msg) { is CounterMessage.Increment -> counter++ is CounterMessage.GetValue -> msg.response.complete(counter) } } } val counter = counterActor() // Send messages to actor val incrementJobs = List(10) { launch { repeat(1000) { counter.send(CounterMessage.Increment) } } } incrementJobs.forEach { it.join() } // Get final value val response = CompletableDeferred<Int>() counter.send(CounterMessage.GetValue(response)) println("Counter = ${response.await()}") counter.close() } // Pipeline pattern with channels fun pipelinePattern() = runBlocking { // First stage: generate numbers fun CoroutineScope.produceNumbers() = produce<Int> { var x = 1 while (true) { send(x++) delay(100) } } // Second stage: square numbers fun CoroutineScope.square(numbers: ReceiveChannel<Int>) = produce<Int> { for (x in numbers) { send(x * x) } } // Third stage: filter even squares fun CoroutineScope.filterEven(squares: ReceiveChannel<Int>) = produce<Int> { for (x in squares) { if (x % 2 == 0) { send(x) } } } val numbers = produceNumbers() val squares = square(numbers) val evens = filterEven(squares) // Consume the pipeline repeat(5) { println(evens.receive()) } coroutineContext.cancelChildren() // Cancel all child coroutines } // Fan-out and fan-in patterns fun fanOutFanIn() = runBlocking { // Producer fun producer() = produce<Int> { var x = 1 while (true) { send(x++) delay(100) } } // Processor suspend fun process(id: Int, channel: ReceiveChannel<Int>) { for (msg in channel) { println("Processor $id processing $msg") delay(200) // Simulate processing time } } val producerChannel = producer() // Fan-out: multiple consumers from one producer repeat(3) { id -> launch { process(id, producerChannel) } } delay(1000) producerChannel.cancel() // Fan-in: multiple producers to one consumer suspend fun sendNumbers(channel: SendChannel<Int>, start: Int) { var x = start while (true) { channel.send(x) x += 2 delay(100) } } val consumerChannel = Channel<Int>() // Multiple producers repeat(3) { id -> launch { sendNumbers(consumerChannel, id + 1) } } // Single consumer launch { for (msg in consumerChannel) { println("Consumed: $msg") } } delay(1000) consumerChannel.cancel() }
Testing Coroutines and Best Practices
// Testing coroutines with TestCoroutineDispatcher // Add dependency: testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test' class CoroutineTestExample { // @Test fun testCoroutine() = runTest { // runTest from kotlinx-coroutines-test val testDispatcher = StandardTestDispatcher() val viewModel = MyViewModel(testDispatcher) // Test async operation viewModel.loadData() // Advance time advanceTimeBy(1000) // Verify results // assertEquals("Expected data", viewModel.data.value) } } class MyViewModel(private val dispatcher: CoroutineDispatcher) { private val scope = CoroutineScope(dispatcher) fun loadData() { scope.launch { // Simulate loading data delay(1000) // Update state } } } // Best practices for coroutines fun coroutineBestPractices() { // 1. Use structured concurrency class ProperScopeUsage { private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob()) fun doWork() { // Good: coroutine is properly scoped scope.launch { val data = withContext(Dispatchers.IO) { fetchData() } processData(data) } } fun cleanup() { scope.cancel() } } // 2. Handle exceptions properly class ExceptionHandling { private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob()) private val exceptionHandler = CoroutineExceptionHandler { _, e -> println("Caught exception: $e") } fun safeOperation() { scope.launch(exceptionHandler) { try { riskyCall() } catch (e: Exception) { // Handle expected exceptions println("Expected exception: $e") } } } } // 3. Choose the right dispatcher fun dispatcherSelection() = runBlocking { // CPU-bound work launch(Dispatchers.Default) { heavyComputation() } // IO-bound work launch(Dispatchers.IO) { networkCall() } // UI updates (Android) // launch(Dispatchers.Main) { // updateUI() // } } // 4. Use timeouts for network operations suspend fun <T> withTimeoutSafe( timeout: Long = 5000, block: suspend () -> T ): Result<T> = try { Result.success(withTimeout(timeout) { block() }) } catch (e: TimeoutCancellationException) { Result.failure(e) } catch (e: Exception) { Result.failure(e) } // 5. Avoid blocking in coroutines suspend fun properSuspension() { // Good: use delay (suspending) delay(1000) // Bad: don't use Thread.sleep (blocking) // Thread.sleep(1000) } // 6. Use flows for streams of data fun flowForStreams(): Flow<String> = flow { var counter = 0 while (true) { emit("Item $counter") delay(1000) counter++ } } // 7. Proper resource cleanup class ResourceManagement { suspend fun useResource() { val resource = acquireResource() try { // Use resource resource.useIt() } finally { resource.close() } } // Or use use extension function suspend fun useResourceBetter() { acquireResource().use { resource -> resource.useIt() } } } } // Debugging coroutines fun debuggingCoroutines() = runBlocking { // Enable coroutine debugging System.setProperty("kotlinx.coroutines.debug", "on") // Use coroutine names for debugging launch(CoroutineName("NetworkCall")) { println("Running in: ${coroutineContext[CoroutineName]}") // Network operation } // Log coroutine context launch { println("Coroutine context: $coroutineContext") } // Use async for parallel operations with proper error handling val deferred1 = async(CoroutineName("Operation1")) { operation1() } val deferred2 = async(CoroutineName("Operation2")) { operation2() } try { val results = awaitAll(deferred1, deferred2) println("Results: $results") } catch (e: Exception) { println("One of the operations failed: $e") } } suspend fun operation1(): String { delay(500) return "Result1" } suspend fun operation2(): String { delay(700) return "Result2" } // Mocking coroutines in tests interface DataRepository { suspend fun fetchData(): String } class FakeRepository : DataRepository { override suspend fun fetchData(): String { delay(100) // Simulate delay return "Fake data" } } class ViewModel(private val repository: DataRepository) { private val scope = CoroutineScope(Dispatchers.Main) fun loadData(callback: (String) -> Unit) { scope.launch { val data = repository.fetchData() callback(data) } } }
5. Android Development with Kotlin
Android Fundamentals with Kotlin
// Basic Android Activity with Kotlin class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private val viewModel: MainViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // View Binding binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) setupUI() setupObservers() } private fun setupUI() { binding.button.setOnClickListener { viewModel.onButtonClicked() } binding.recyclerView.apply { layoutManager = LinearLayoutManager(this@MainActivity) adapter = MyAdapter() } } private fun setupObservers() { viewModel.uiState.observe(this) { state -> when (state) { is UiState.Loading -> showLoading() is UiState.Success -> showData(state.data) is UiState.Error -> showError(state.message) } } viewModel.events.observe(this) { event -> when (event) { is MainEvent.NavigateToDetails -> navigateToDetails(event.itemId) } } } private fun showLoading() { binding.progressBar.visibility = View.VISIBLE binding.content.visibility = View.GONE } private fun showData(data: List<Item>) { binding.progressBar.visibility = View.GONE binding.content.visibility = View.VISIBLE (binding.recyclerView.adapter as MyAdapter).submitList(data) } private fun showError(message: String) { Toast.makeText(this, message, Toast.LENGTH_SHORT).show() } private fun navigateToDetails(itemId: String) { val intent = Intent(this, DetailActivity::class.java).apply { putExtra("ITEM_ID", itemId) } startActivity(intent) } } // ViewModel with Kotlin class MainViewModel : ViewModel() { private val repository = DataRepository() private val _uiState = MutableStateFlow<UiState<List<Item>>>(UiState.Loading) val uiState: StateFlow<UiState<List<Item>>> = _uiState.asStateFlow() private val _events = MutableSharedFlow<MainEvent>() val events: SharedFlow<MainEvent> = _events.asSharedFlow() init { loadData() } fun onButtonClicked() { viewModelScope.launch { _events.emit(MainEvent.NavigateToDetails("123")) } } private fun loadData() { viewModelScope.launch { _uiState.value = UiState.Loading try { val data = repository.getItems() _uiState.value = UiState.Success(data) } catch (e: Exception) { _uiState.value = UiState.Error(e.message ?: "Unknown error") } } } } // Sealed classes for state and events sealed class UiState<out T> { object Loading : UiState<Nothing>() data class Success<T>(val data: T) : UiState<T>() data class Error(val message: String) : UiState<Nothing>() } sealed class MainEvent { data class NavigateToDetails(val itemId: String) : MainEvent() } // RecyclerView Adapter with Kotlin class MyAdapter : ListAdapter<Item, MyAdapter.ViewHolder>(DiffCallback) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val binding = ItemLayoutBinding.inflate( LayoutInflater.from(parent.context), parent, false ) return ViewHolder(binding) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val item = getItem(position) holder.bind(item) } inner class ViewHolder( private val binding: ItemLayoutBinding ) : RecyclerView.ViewHolder(binding.root) { fun bind(item: Item) { binding.title.text = item.title binding.description.text = item.description binding.root.setOnClickListener { // Handle click } } } companion object { private val DiffCallback = object : DiffUtil.ItemCallback<Item>() { override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean { return oldItem.id == newItem.id } override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean { return oldItem == newItem } } } } // Data classes for models data class Item( val id: String, val title: String, val description: String, val imageUrl: String, val createdAt: Date )
Jetpack Compose - Modern UI Toolkit
// Basic Composable function @Composable fun Greeting(name: String) { Text( text = "Hello $name!", style = MaterialTheme.typography.h4, color = MaterialTheme.colors.primary ) } // Composable with state @Composable fun Counter() { var count by remember { mutableStateOf(0) } Column( modifier = Modifier .fillMaxSize() .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text( text = "Count: $count", style = MaterialTheme.typography.h3 ) Spacer(modifier = Modifier.height(16.dp)) Row { Button( onClick = { count++ }, modifier = Modifier.padding(8.dp) ) { Text("Increment") } Button( onClick = { count-- }, modifier = Modifier.padding(8.dp) ) { Text("Decrement") } Button( onClick = { count = 0 }, modifier = Modifier.padding(8.dp) ) { Text("Reset") } } } } // List composable with LazyColumn @Composable fun ItemList(items: List<Item>) { LazyColumn( modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { items(items) { item -> ItemCard(item = item) } } } @Composable fun ItemCard(item: Item) { Card( modifier = Modifier .fillMaxWidth() .clickable { /* Handle click */ }, elevation = 4.dp, shape = MaterialTheme.shapes.medium ) { Column( modifier = Modifier.padding(16.dp) ) { Text( text = item.title, style = MaterialTheme.typography.h6, color = MaterialTheme.colors.onSurface ) Spacer(modifier = Modifier.height(4.dp)) Text( text = item.description, style = MaterialTheme.typography.body2, color = MaterialTheme.colors.onSurface.copy(alpha = 0.7f) ) Spacer(modifier = Modifier.height(8.dp)) Row( verticalAlignment = Alignment.CenterVertically ) { Icon( imageVector = Icons.Default.Favorite, contentDescription = "Favorite", tint = MaterialTheme.colors.primary, modifier = Modifier.size(16.dp) ) Spacer(modifier = Modifier.width(4.dp)) Text( text = "Like", style = MaterialTheme.typography.caption, color = MaterialTheme.colors.primary ) } } } } // ViewModel integration with Compose @Composable fun MainScreen( viewModel: MainViewModel = hiltViewModel() ) { val uiState by viewModel.uiState.collectAsState() Scaffold( topBar = { TopAppBar( title = { Text("My App") }, actions = { IconButton(onClick = { viewModel.onSettingsClicked() }) { Icon(Icons.Default.Settings, contentDescription = "Settings") } } ) }, floatingActionButton = { FloatingActionButton(onClick = { viewModel.onFabClicked() }) { Icon(Icons.Default.Add, contentDescription = "Add") } } ) { paddingValues -> when (val state = uiState) { is UiState.Loading -> LoadingScreen() is UiState.Success -> ItemList( items = state.data, modifier = Modifier.padding(paddingValues) ) is UiState.Error -> ErrorScreen( message = state.message, onRetry = { viewModel.loadData() }, modifier = Modifier.padding(paddingValues) ) } } } @Composable fun LoadingScreen() { Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { CircularProgressIndicator() } } @Composable fun ErrorScreen( message: String, onRetry: () -> Unit, modifier: Modifier = Modifier ) { Column( modifier = modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Icon( imageVector = Icons.Default.Error, contentDescription = "Error", tint = MaterialTheme.colors.error, modifier = Modifier.size(64.dp) ) Spacer(modifier = Modifier.height(16.dp)) Text( text = message, style = MaterialTheme.typography.body1, textAlign = TextAlign.Center ) Spacer(modifier = Modifier.height(16.dp)) Button(onClick = onRetry) { Text("Retry") } } } // Navigation with Compose @Composable fun AppNavigation() { val navController = rememberNavController() NavHost( navController = navController, startDestination = "main" ) { composable("main") { MainScreen { itemId -> navController.navigate("details/$itemId") } } composable( "details/{itemId}", arguments = listOf(navArgument("itemId") { type = NavType.StringType }) ) { backStackEntry -> val itemId = backStackEntry.arguments?.getString("itemId") ?: "" DetailScreen(itemId = itemId) { navController.popBackStack() } } } } // Theme customization @Composable fun MyAppTheme( darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit ) { val colors = if (darkTheme) { DarkColorPalette } else { LightColorPalette } MaterialTheme( colors = colors, typography = MyTypography, shapes = MyShapes, content = content ) }
Dependency Injection with Hilt
// Application setup @HiltAndroidApp class MyApplication : Application() // Module definitions @Module @InstallIn(SingletonComponent::class) object AppModule { @Provides @Singleton fun provideRetrofit(): Retrofit { return Retrofit.Builder() .baseUrl("https://api.example.com/") .addConverterFactory(GsonConverterFactory.create()) .build() } @Provides @Singleton fun provideApiService(retrofit: Retrofit): ApiService { return retrofit.create(ApiService::class.java) } @Provides @Singleton fun provideDatabase(@ApplicationContext context: Context): AppDatabase { return Room.databaseBuilder( context, AppDatabase::class.java, "app-database" ).build() } } @Module @InstallIn(ViewModelComponent::class) object ViewModelModule { @Provides fun provideRepository( apiService: ApiService, database: AppDatabase ): DataRepository { return DataRepositoryImpl(apiService, database) } } // Repository implementation class DataRepositoryImpl @Inject constructor( private val apiService: ApiService, private val database: AppDatabase ) : DataRepository { override suspend fun getItems(): List<Item> { return try { // Try to get from network first val networkItems = apiService.getItems() // Cache to database database.itemDao().insertAll(networkItems) networkItems } catch (e: Exception) { // Fall back to local database database.itemDao().getAll() } } } // Hilt ViewModel @HiltViewModel class MainViewModel @Inject constructor( private val repository: DataRepository ) : ViewModel() { private val _uiState = MutableStateFlow<UiState<List<Item>>>(UiState.Loading) val uiState: StateFlow<UiState<List<Item>>> = _uiState.asStateFlow() init { loadData() } fun loadData() { viewModelScope.launch { _uiState.value = UiState.Loading try { val items = repository.getItems() _uiState.value = UiState.Success(items) } catch (e: Exception) { _uiState.value = UiState.Error(e.message ?: "Unknown error") } } } } // Hilt in Activities and Fragments @AndroidEntryPoint class MainActivity : AppCompatActivity() { // Injected by Hilt @Inject lateinit var analytics: AnalyticsService private val viewModel: MainViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... } } // Custom scopes and qualifiers @Qualifier @Retention(AnnotationRetention.BINARY) annotation class AuthInterceptorOkHttpClient @Qualifier @Retention(AnnotationRetention.BINARY) annotation class OtherInterceptorOkHttpClient @Module @InstallIn(SingletonComponent::class) object NetworkModule { @AuthInterceptorOkHttpClient @Provides @Singleton fun provideAuthOkHttpClient(authInterceptor: AuthInterceptor): OkHttpClient { return OkHttpClient.Builder() .addInterceptor(authInterceptor) .build() } @OtherInterceptorOkHttpClient @Provides @Singleton fun provideOtherOkHttpClient(otherInterceptor: OtherInterceptor): OkHttpClient { return OkHttpClient.Builder() .addInterceptor(otherInterceptor) .build() } @Provides @Singleton fun provideAuthRetrofit( @AuthInterceptorOkHttpClient client: OkHttpClient ): Retrofit { return Retrofit.Builder() .baseUrl("https://auth.api.example.com/") .client(client) .addConverterFactory(GsonConverterFactory.create()) .build() } } // Testing with Hilt @HiltAndroidTest class MainActivityTest { @get:Rule var hiltRule = HiltAndroidRule(this) @Inject lateinit var repository: DataRepository @Before fun init() { hiltRule.inject() } @Test fun testActivity() { // Test with injected dependencies } }
Advanced Android Patterns
// Clean Architecture layers // Domain layer (business logic) data class User( val id: String, val name: String, val email: String ) interface UserRepository { suspend fun getUser(id: String): User? suspend fun saveUser(user: User) } class GetUserUseCase( private val userRepository: UserRepository ) { suspend operator fun invoke(id: String): User? { return userRepository.getUser(id) } } class SaveUserUseCase( private val userRepository: UserRepository ) { suspend operator fun invoke(user: User) { userRepository.saveUser(user) } } // Data layer (data sources) class UserRepositoryImpl( private val localDataSource: UserLocalDataSource, private val remoteDataSource: UserRemoteDataSource ) : UserRepository { override suspend fun getUser(id: String): User? { return try { // Try remote first val remoteUser = remoteDataSource.getUser(id) remoteUser?.let { localDataSource.saveUser(it) } remoteUser } catch (e: Exception) { // Fall back to local localDataSource.getUser(id) } } override suspend fun saveUser(user: User) { localDataSource.saveUser(user) // Optionally sync with remote } } interface UserLocalDataSource { suspend fun getUser(id: String): User? suspend fun saveUser(user: User) } interface UserRemoteDataSource { suspend fun getUser(id: String): User? } // Room Database implementation @Entity(tableName = "users") data class UserEntity( @PrimaryKey val id: String, val name: String, val email: String ) @Dao interface UserDao { @Query("SELECT * FROM users WHERE id = :id") suspend fun getUser(id: String): UserEntity? @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun saveUser(user: UserEntity) @Query("DELETE FROM users") suspend fun clearAll() } // Retrofit API implementation interface UserApiService { @GET("users/{id}") suspend fun getUser(@Path("id") id: String): UserResponse? } data class UserResponse( val id: String, val name: String, val email: String ) { fun toDomain(): User = User(id, name, email) fun toEntity(): UserEntity = UserEntity(id, name, email) } // WorkManager for background tasks class SyncWorker( context: Context, params: WorkerParameters, private val userRepository: UserRepository ) : CoroutineWorker(context, params) { override suspend fun doWork(): Result { return try { // Perform sync operation syncData() Result.success() } catch (e: Exception) { if (runAttemptCount < 3) { Result.retry() } else { Result.failure() } } } private suspend fun syncData() { // Implementation } } // Push notifications with FCM class MyFirebaseMessagingService : FirebaseMessagingService() { override fun onMessageReceived(remoteMessage: RemoteMessage) { super.onMessageReceived(remoteMessage) remoteMessage.notification?.let { notification -> showNotification( title = notification.title ?: "", body = notification.body ?: "" ) } } private fun showNotification(title: String, body: String) { val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( "default", "Default Channel", NotificationManager.IMPORTANCE_DEFAULT ) notificationManager.createNotificationChannel(channel) } val notification = NotificationCompat.Builder(this, "default") .setContentTitle(title) .setContentText(body) .setSmallIcon(R.drawable.ic_notification) .setAutoCancel(true) .build() notificationManager.notify(1, notification) } } // Security best practices class SecurePreferences(context: Context) { private val sharedPreferences = context.getSharedPreferences("secure_prefs", Context.MODE_PRIVATE) fun saveEncryptedValue(key: String, value: String) { val encryptedValue = encrypt(value) sharedPreferences.edit().putString(key, encryptedValue).apply() } fun getEncryptedValue(key: String): String? { val encryptedValue = sharedPreferences.getString(key, null) return encryptedValue?.let { decrypt(it) } } private fun encrypt(value: String): String { // Implementation using Android Keystore return value // Simplified } private fun decrypt(encryptedValue: String): String { // Implementation return encryptedValue // Simplified } } // Performance optimization class ImageLoader(private val context: Context) { private val memoryCache = LruCache<String, Bitmap>(calculateMemoryCacheSize()) suspend fun loadImage(url: String): Bitmap? = withContext(Dispatchers.IO) { // Check memory cache first memoryCache[url]?.let { return@withContext it } // Check disk cache val diskCache = getFromDiskCache(url) diskCache?.let { memoryCache.put(url, it) return@withContext it } // Load from network val networkBitmap = loadFromNetwork(url) networkBitmap?.let { saveToDiskCache(url, it) memoryCache.put(url, it) } networkBitmap } private fun calculateMemoryCacheSize(): Int { val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt() return maxMemory / 8 // Use 1/8th of available memory } }
6. Advanced Kotlin Features
DSLs (Domain Specific Languages)
// HTML Builder DSL fun html(block: HTML.() -> Unit): HTML { return HTML().apply(block) } class HTML { private val children = mutableListOf<Element>() fun head(block: Head.() -> Unit) { children.add(Head().apply(block)) } fun body(block: Body.() -> Unit) { children.add(Body().apply(block)) } override fun toString(): String { return """<!DOCTYPE html> <html> ${children.joinToString("\n")} </html>""" } } class Head { private val children = mutableListOf<Element>() fun title(text: String) { children.add(Tag("title", text)) } fun script(src: String) { children.add(EmptyTag("script", mapOf("src" to src))) } override fun toString(): String { return """<head> ${children.joinToString("\n") { " $it" }} </head>""" } } class Body { private val children = mutableListOf<Element>() fun h1(text: String) { children.add(Tag("h1", text)) } fun p(block: P.() -> Unit) { children.add(P().apply(block)) } fun div(block: Div.() -> Unit) { children.add(Div().apply(block)) } override fun toString(): String { return """<body> ${children.joinToString("\n") { " $it" }} </body>""" } } class P { private var text = "" operator fun String.unaryPlus() { text += this } override fun toString() = """<p>$text</p>""" } class Div { private val children = mutableListOf<Element>() private val attributes = mutableMapOf<String, String>() fun id(value: String) { attributes["id"] = value } fun className(value: String) { attributes["class"] = value } fun p(block: P.() -> Unit) { children.add(P().apply(block)) } override fun toString(): String { val attrs = attributes.entries.joinToString(" ") { """${it.key}="${it.value}"""" } return """<div$attrs> ${children.joinToString("\n") { " $it" }} </div>""" } } sealed class Element class Tag(val name: String, val content: String) : Element() { override fun toString() = """<$name>$content</$name>""" } class EmptyTag(val name: String, val attributes: Map<String, String> = emptyMap()) : Element() { override fun toString(): String { val attrs = attributes.entries.joinToString(" ") { """${it.key}="${it.value}"""" } return """<$name$attrs />""" } } // Usage of HTML DSL fun createHTMLPage() = html { head { title("My Page") script("app.js") } body { h1("Welcome to Kotlin DSL") div { id("content") className("container") p { +"This is a paragraph created with " +"Kotlin DSL" } } } } // Gradle Kotlin DSL style class Dependencies { private val implementations = mutableListOf<String>() private val testImplementations = mutableListOf<String>() fun implementation(dependency: String) { implementations.add(dependency) } fun testImplementation(dependency: String) { testImplementations.add(dependency) } override fun toString(): String { return """dependencies { ${implementations.joinToString("\n ") { "implementation(""""$it"""")" }} ${testImplementations.joinToString("\n ") { "testImplementation(""""$it"""")" }} }""" } } fun dependencies(block: Dependencies.() -> Unit): String { return Dependencies().apply(block).toString() } // Usage val deps = dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.0") implementation("androidx.core:core-ktx:1.12.0") testImplementation("junit:junit:4.13.2") }
Metaprogramming with Annotations and Reflection
// Custom annotations @Target(AnnotationTarget.CLASS) annotation class Entity(val tableName: String) @Target(AnnotationTarget.PROPERTY) annotation class Column(val name: String) @Target(AnnotationTarget.PROPERTY) annotation class Id @Target(AnnotationTarget.FUNCTION) annotation class Test(val priority: Int = 1) // Using annotations @Entity(tableName = "users") data class User( @Id val id: Long, @Column(name = "user_name") val name: String, @Column(name = "email_address") val email: String, val age: Int // No annotation - uses property name ) class TestSuite { @Test(priority = 1) fun testBasicFunctionality() { // Test implementation } @Test(priority = 2) fun testAdvancedFeatures() { // Test implementation } } // Reflection examples fun reflectionExamples() { val user = User(1, "John Doe", "john@example.com", 30) // Class reference val kClass = user::class println("Class name: ${kClass.simpleName}") println("Properties:") // Iterate through properties kClass.memberProperties.forEach { property -> println(" ${property.name} = ${property.get(user)}") } // Find annotations kClass.annotations.forEach { annotation -> when (annotation) { is Entity -> println("Table name: ${annotation.tableName}") } } // Call function by name val method = kClass.members.find { it.name == "toString" } val result = method?.call(user) println("toString result: $result") } // Annotation processing with kapt (Kotlin Annotation Processing Tool) // Note: This would typically be in a separate processor module // Simple ORM using reflection class SimpleORM { fun <T : Any> createTable(entity: T): String { val kClass = entity::class val tableName = getTableName(kClass) val columns = getColumns(kClass) return """CREATE TABLE $tableName ( ${columns.joinToString(",\n ")} );""" } fun <T : Any> insert(entity: T): String { val kClass = entity::class val tableName = getTableName(kClass) val columns = getColumnNames(kClass) val values = getColumnValues(kClass, entity) return """INSERT INTO $tableName (${columns.joinToString()}) VALUES (${values.joinToString()});""" } private fun <T : Any> getTableName(kClass: KClass<T>): String { return kClass.annotations .filterIsInstance<Entity>() .firstOrNull() ?.tableName ?: kClass.simpleName!!.lowercase() } private fun <T : Any> getColumns(kClass: KClass<T>): List<String> { return kClass.memberProperties.map { property -> val columnName = property.annotations .filterIsInstance<Column>() .firstOrNull() ?.name ?: property.name val type = when (property.returnType.classifier) { Long::class -> "BIGINT" String::class -> "VARCHAR(255)" Int::class -> "INT" else -> "TEXT" } val constraints = if (property.annotations.any { it is Id }) { " PRIMARY KEY" } else { "" } """$columnName $type$constraints""" } } private fun <T : Any> getColumnNames(kClass: KClass<T>): List<String> { return kClass.memberProperties.map { property -> property.annotations .filterIsInstance<Column>() .firstOrNull() ?.name ?: property.name } } private fun <T : Any> getColumnValues(kClass: KClass<T>, entity: T): List<String> { return kClass.memberProperties.map { property -> val value = property.get(entity) when (value) { is String -> """'$value'""" else -> value.toString() } } } } // Usage of SimpleORM fun useSimpleORM() { val user = User(1, "John Doe", "john@example.com", 30) val orm = SimpleORM() println(orm.createTable(user)) println(orm.insert(user)) }
Kotlin Multiplatform (KMP)
// Shared common module // In commonMain source set expect class Platform() { val platform: String } class Greeting { private val platform: Platform = Platform() fun greet(): String { return "Hello, ${platform.platform}!" } } // Platform-specific implementations // In androidMain source set actual class Platform actual constructor() { actual val platform: String = "Android" } // In iosMain source set actual class Platform actual constructor() { actual val platform: String = "iOS" } // In jsMain source set actual class Platform actual constructor() { actual val platform: String = "JavaScript" } // Shared business logic expect suspend fun readFile(path: String): String? expect suspend fun writeFile(path: String, content: String) class FileManager { suspend fun processFile(inputPath: String, outputPath: String) { val content = readFile(inputPath) ?: return val processed = processContent(content) writeFile(outputPath, processed) } private fun processContent(content: String): String { // Shared processing logic return content.uppercase() } } // Shared networking with Ktor expect fun httpClient(config: HttpClientConfig<*>.() -> Unit): HttpClient class ApiClient(private val baseUrl: String) { private val client = httpClient { install(JsonFeature) { serializer = KotlinxSerializer() } } suspend fun fetchData(): Data { return client.get<Data>("$baseUrl/data") } suspend fun postData(data: Data) { client.post<Unit>("$baseUrl/data") { body = data } } } // Shared data models @Serializable data class Data( val id: String, val title: String, val content: String, val timestamp: Long ) // Shared repository pattern interface DataRepository { suspend fun getData(): List<Data> suspend fun saveData(data: Data) suspend fun deleteData(id: String) } class DataRepositoryImpl( private val apiClient: ApiClient, private val localDataSource: LocalDataSource ) : DataRepository { override suspend fun getData(): List<Data> { return try { // Try to fetch from network val remoteData = apiClient.fetchData() localDataSource.saveAll(remoteData) remoteData } catch (e: Exception) { // Fall back to local data localDataSource.getAll() } } override suspend fun saveData(data: Data) { localDataSource.save(data) // Optionally sync with backend try { apiClient.postData(data) } catch (e: Exception) { // Handle sync error println("Sync failed: ${e.message}") } } override suspend fun deleteData(id: String) { localDataSource.delete(id) } } // Expect/actual for dependency injection expect class PlatformDependencies { val logger: Logger val fileSystem: FileSystem } // Android implementation actual class PlatformDependencies actual constructor() { actual val logger: Logger = AndroidLogger() actual val fileSystem: FileSystem = AndroidFileSystem() } // iOS implementation actual class PlatformDependencies actual constructor() { actual val logger: Logger = IOSLogger() actual val fileSystem: FileSystem = IOSFileSystem() } // Building a multiplatform library class MyMultiplatformLibrary( private val dependencies: PlatformDependencies ) { fun doSomething() { dependencies.logger.info("Doing something...") // Cross-platform logic } } // Testing in multiplatform class CommonTest { @Test fun testCommonLogic() { val library = MyMultiplatformLibrary(FakeDependencies()) // Test common logic } } class FakeDependencies : PlatformDependencies() { override val logger: Logger = FakeLogger() override val fileSystem: FileSystem = FakeFileSystem() }
Performance Optimization and Best Practices
// Performance optimization techniques // 1. Use inline functions for higher-order functions inline fun <T> measureTime(block: () -> T): Pair<T, Long> { val start = System.nanoTime() val result = block() val end = System.nanoTime() return result to (end - start) } // 2. Use sequences for large collections fun processLargeDataset() { val largeList = (1..1_000_000).toList() // Bad: creates intermediate collections val result1 = largeList .filter { it % 2 == 0 } // Creates 500,000 element list .map { it * it } // Creates another 500,000 element list .take(1000) // Takes first 1000 .toList() // Good: uses sequences (lazy evaluation) val result2 = largeList .asSequence() // Convert to sequence .filter { it % 2 == 0 } // Lazy operation .map { it * it } // Lazy operation .take(1000) // Only processes needed elements .toList() // Terminal operation } // 3. Avoid unnecessary object creation class Point(val x: Int, val y: Int) { // Companion object with cached instances for common values companion object { val ORIGIN = Point(0, 0) val UNIT_X = Point(1, 0) val UNIT_Y = Point(0, 1) } } // 4. Use value classes for wrapper types @JvmInline value class UserId(val value: Long) @JvmInline value class Email(val value: String) // These have no runtime overhead compared to using primitives directly // 5. Optimize string operations fun stringOptimization() { // Bad: creates intermediate strings var result = "" for (i in 1..1000) { result += i // Creates new string each time } // Good: use StringBuilder val builder = StringBuilder() for (i in 1..1000) { builder.append(i) } val optimizedResult = builder.toString() // Even better: use buildString val bestResult = buildString { for (i in 1..1000) { append(i) } } } // 6. Memory-efficient data structures fun memoryEfficientCollections() { // Use primitive arrays when possible val intArray = IntArray(1000) // Uses primitive ints val integerList = List(1000) { it } // Uses boxed Integers // Use appropriate collection types val frequentLookup = hashSetOf(1, 2, 3) // O(1) lookup val sortedValues = sortedSetOf(3, 1, 2) // O(log n) lookup but sorted // Use lazy initialization val heavyObject by lazy { HeavyObject() // Only created when first accessed } } // 7. Coroutine performance suspend fun coroutinePerformance() { // Use appropriate dispatchers withContext(Dispatchers.Default) { // CPU-intensive work heavyComputation() } withContext(Dispatchers.IO) { // IO-bound work readLargeFile() } // Use channels for communication between coroutines val channel = Channel<Data>() // Producer launch { for (i in 1..1000) { channel.send(Data(i)) } channel.close() } // Consumer launch { for (data in channel) { process(data) } } } // 8. Cache expensive computations class ExpensiveComputation { private val cache = mutableMapOf<Int, String>() fun compute(input: Int): String { return cache.getOrPut(input) { // Expensive computation Thread.sleep(100) "result_$input" } } } // 9. Avoid boxing in performance-critical code fun avoidBoxing() { // Primitive arrays val primitiveArray = intArrayOf(1, 2, 3) // No boxing val boxedArray = arrayOf(1, 2, 3) // Boxed Integers // Use value classes data class Boxed(val value: Int) // Regular class @JvmInline value class Inline(val value: Int) // No runtime overhead } // 10. Profile and measure fun profilingExample() { // Use measureTimeMillis for quick measurements val time = measureTimeMillis { expensiveOperation() } println("Operation took ${time}ms") // Use profiling tools for detailed analysis // - Android Profiler // - YourKit // - Java Mission Control } // Best practices for production code // 1. Use sealed classes for state management sealed class NetworkResult<out T> { object Loading : NetworkResult<Nothing>() data class Success<T>(val data: T) : NetworkResult<T>() data class Error(val message: String, val code: Int) : NetworkResult<Nothing>() } // 2. Prefer immutability data class User( val id: String, // Immutable val name: String, // Immutable val email: String // Immutable ) { // Provide copy methods for "modifications" fun withName(newName: String) = copy(name = newName) fun withEmail(newEmail: String) = copy(email = newEmail) } // 3. Use extension functions wisely fun String.isValidEmail(): Boolean { return contains("@") && length > 5 } fun List<String>.filterValidEmails(): List<String> { return filter { it.isValidEmail() } } // 4. Proper error handling suspend fun <T> safeApiCall(block: suspend () -> T): Result<T> { return try { Result.success(block()) } catch (e: Exception) { Result.failure(e) } } // 5. Resource management fun readFileSafely(path: String): String? { return File(path).takeIf { it.exists() }?.use { file -> file.readText() } }
💻 Kotlin Practice Projects
Beginner Level
- 1Build a Command Line Calculator with basic operations
- 2Create a Todo List Application with file persistence
- 3Implement a Number Guessing Game with user input
- 4Build a Simple Bank Account Management System
- 5Create a Temperature Converter between Celsius/Fahrenheit
Intermediate Level
- 1Develop a REST API Client with error handling
- 2Build a Chat Application with WebSocket connections
- 3Create a Budget Tracker with data visualization
- 4Implement a Weather App with API integration
- 5Build a Note-taking App with search and categories
Advanced Level
- 1Create a Multiplatform Mobile App with KMP
- 2Build a Microservices Architecture with Ktor
- 3Develop a Custom DSL for configuration files
- 4Implement a Caching System with expiration
- 5Build a Real-time Dashboard with coroutines and flows
📋 Kotlin Quick Reference
Core Concepts
- •val/var - Immutable/mutable variables
- •fun - Function declaration
- •class/data class - Class definitions
- •null safety - ?. ?: !! operators
- •Extension functions - Adding methods to existing classes
- •Lambda expressions - { param -> body }
- •Coroutines - Async programming
Common Patterns
- •Sealed classes - Restricted hierarchies
- •Builder pattern - DSL creation
- •Repository pattern - Data abstraction
- •Dependency Injection - Hilt/Koin
- •MVVM Architecture - ViewModel + LiveData
- •Functional programming - map/filter/reduce
- •Error handling - Result/Try-catch
Master the Language That's Revolutionizing Development!
Kotlin has transformed modern software development with its concise syntax, powerful features, and excellent tooling support. From Android apps to backend services and cross-platform solutions, Kotlin provides a unified development experience that boosts productivity and code quality.
Understanding Kotlin opens doors to Android development, server-side programming, and multiplatform projects. Its interoperability with Java, null safety features, and coroutine support make it an ideal choice for building robust, maintainable applications across all platforms.