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.