Section 1: Kotlin Fundamentals

Detailed Explanations with Complete Code Examples


1.1 Basic Syntax & Hello World

Core Concepts Explained

Kotlin’s syntax is concise and expressive. Here’s what you need to know:

  1. val vs var:
    • Use val for read-only (immutable) variables.
    • Use var for mutable variables (values can change).
  2. Type Inference: Kotlin infers types automatically (no need to declare String or Int explicitly).
  3. String Templates: Embed variables directly into strings using $ or ${}.
  4. Functions: Declare functions with fun. Single-expression functions omit braces.

Complete Code Example

data class Person(val name: String, val age: Int) // Data class for structured data
 
fun main() {
    // Variables: val (immutable) vs var (mutable)
    val greeting = "Hello, Kotlin!" // Type inferred as String
    var counter = 0                  // Mutable counter
    counter += 1                     // Valid: var can change
    
    // String templates
    val person = Person("Alice", 29)
    println("${person.name} is ${person.age} years old") // "Alice is 29 years old"
    
    // Function with parameters and return type
    fun add(a: Int, b: Int): Int {
        return a + b
    }
    println(add(5, 3)) // 8
    
    // Single-expression function (shorthand)
    fun isAdult(person: Person) = person.age >= 18
    println(isAdult(person)) // true
}

1.2 Null Safety

Core Concepts Explained

Kotlin eliminates NullPointerException (NPE) risks by design:

  1. Nullable Types: Explicitly declare nullable variables with ? (e.g., String?).
  2. Safe Calls (?.): Access properties/methods safely without NPEs.
  3. Elvis Operator (?:): Provide default values for nullable expressions.
  4. Non-null Assertion (!!): Forcefully unwrap nullable types (use sparingly!).
  5. Scope Functions: Use let{} to execute code only if a value is non-null.

Complete Code Example

data class Address(val street: String?, val zipCode: Int?) // Street can be null
 
fun main() {
    // Nullable type example
    val nullableString: String? = null
    
    // Safe call: returns null if nullableString is null
    println(nullableString?.length) // null
    
    // Elvis operator: default value if null
    val length = nullableString?.length ?: 0
    println(length) // 0
    
    // Risky non-null assertion (throws NPE if null)
    val forcedLength: String? = "Kotlin"
    println(forcedLength!!.length) // 6
    
    // let{} for safe scoping
    val address = Address(null, 12345)
    address.street?.let { 
        println("Street: $it") // Won't execute (street is null)
    }
    
    // Chaining safe calls with let{}
    val person: Person? = Person("Bob", 31)
    val street = person?.let { 
        // Imagine fetching address from a database
        Address("Main Street", 67890) 
    }?.street ?: "Unknown"
    println(street) // "Main Street"
}

1.3 Conditionals & Loops

Core Concepts Explained

Kotlin simplifies control flow with expressive constructs:

  1. if as Expression: Returns a value (no ternary operator needed).
  2. when: A replacement for switch that works with any data type.
  3. Ranges: Define sequences (e.g., 1..10, 0 until 5).
  4. Loops: for iterates over ranges/collections; while repeats until a condition fails.

Complete Code Example

fun main() {
    val score = 85
    
    // if-else as an expression (returns a value)
    val grade = if (score >= 90) "A"
        else if (score >= 80) "B"
        else "C"
    println(grade) // "B"
    
    // when expression (like switch, but more powerful)
    when (score) {
        in 90..100 -> println("Excellent")
        in 80..89 -> println("Good") // Matches here
        else -> println("Needs improvement")
    }
    
    // Ranges and loops
    for (i in 1..5) print("$i ") // 1 2 3 4 5
    println()
    
    // Step and until (exclusive upper bound)
    for (i in 0 until 10 step 2) print("$i ") // 0 2 4 6 8
    println()
    
    // Iterate over a list
    val names = listOf("Alice", "Bob", "Charlie")
    for (name in names) print("$name ") // Alice Bob Charlie
    println()
    
    // while loop
    var x = 3
    while (x > 0) {
        print("$x ") // 3 2 1
        x--
    }
}

1.4 Collections

Core Concepts Explained

Kotlin provides immutable and mutable collections:

  1. Immutable Collections: Created with listOf(), setOf(), mapOf() (can’t modify after creation).
  2. Mutable Collections: Use mutableListOf(), etc., to add/remove elements.
  3. Common Operations:
    • filter: Select elements matching a condition.
    • map: Transform elements (e.g., extract a property).
    • forEach: Iterate over elements.

Complete Code Example

data class Book(val title: String, val year: Int)
 
fun main() {
    // Immutable list
    val books = listOf(
        Book("1984", 1949),
        Book("The Hobbit", 1937),
        Book("Dune", 1965)
    )
    
    // Filter books published before 1950
    val oldBooks = books.filter { it.year < 1950 }
    println(oldBooks) // [Book(title=1984, year=1949), Book(title=The Hobbit, year=1937)]
    
    // Map to extract titles
    val titles = books.map { it.title }
    println(titles) // [1984, The Hobbit, Dune]
    
    // Mutable list (can add/remove elements)
    val mutableBooks = mutableListOf<Book>()
    mutableBooks.add(Book("Neuromancer", 1984))
    mutableBooks += Book("Snow Crash", 1992)
    println(mutableBooks.size) // 2
    
    // forEach iteration
    books.forEach { println("${it.title} (${it.year})") }
    
    // Find first book matching a condition
    val dune = books.firstOrNull { it.title == "Dune" }
    println(dune) // Book(title=Dune, year=1965)
}

Key Takeaways

ConceptWhy It MattersExample
Immutable valPrevents accidental mutationsval name = "Alice"
Null SafetyEliminates NPEs at compile timeval length = name?.length ?: 0
when ExpressionCleaner than switchwhen (score) { ... }
Collection OperationsFunctional-style data processingbooks.filter { ... }

Section 2: Functional Programming in Kotlin

Detailed Explanations with Complete Code Examples


2.1 Lambdas & Higher-Order Functions

Core Concepts Explained

Functional programming treats functions as first-class citizens. Kotlin embraces this with:

  1. Lambdas: Anonymous functions defined with { }.
    • Syntax: { param -> body } (use it for single parameters).
  2. Higher-Order Functions: Functions that accept or return other functions.
  3. Function Types: Declared as (Type1, Type2) -> ReturnType.

Complete Code Example

data class Product(val name: String, val price: Double)
 
// Higher-order function: Takes a predicate lambda
fun filterProducts(products: List<Product>, predicate: (Product) -> Boolean): List<Product> {
    return products.filter(predicate)
}
 
fun main() {
    val products = listOf(
        Product("Laptop", 999.99),
        Product("Phone", 599.99),
        Product("Headphones", 149.99)
    )
 
    // Lambda 1: Filter affordable products (price < 600)
    val affordable = filterProducts(products) { it.price < 600 }
    println(affordable) // [Product(name=Phone, price=599.99), Product(name=Headphones, price=149.99)]
 
    // Lambda 2: Filter by name
    val headphones = filterProducts(products) { it.name == "Headphones" }
    println(headphones) // [Product(name=Headphones, price=149.99)]
 
    // Function returning a lambda
    val discount = 0.20
    val applyDiscount: (Product) -> Double = { product -> product.price * (1 - discount) }
    println("Discounted price: ${applyDiscount(products[0])}") // 799.992
}

2.2 Extension Functions

Core Concepts Explained

Extension functions let you add methods to existing classes without inheritance:

  1. Syntax: fun ClassName.newMethod() { ... }.
  2. Accessed like regular methods.
  3. Great for utility functions or DSLs.

Complete Code Example

// Adds a "priceWithTax" method to Product
fun Product.priceWithTax(taxRate: Double): Double = this.price * (1 + taxRate)
 
// Adds a "findByName" method to List<Product>
fun List<Product>.findByName(name: String): Product? = this.firstOrNull { it.name == name }
 
fun main() {
    val products = listOf(Product("Laptop", 999.99), Product("Phone", 599.99))
    
    // Use extension function on Product
    val laptop = products[0]
    println("Laptop with tax: ${laptop.priceWithTax(0.10)}") // 1099.989
    
    // Use extension function on List<Product>
    val phone = products.findByName("Phone")
    println(phone) // Product(name=Phone, price=599.99)
}

2.3 Function Composition

Core Concepts Explained

Combine simple functions to create complex logic:

  1. Predicate Composition: Combine conditions using and/or/not.
  2. Custom Combinators: Create reusable logic with helper functions.

Complete Code Example

data class User(val name: String, val age: Int, val isAdmin: Boolean)
 
// Base predicates
val isAdult: (User) -> Boolean = { it.age >= 18 }
val isAdmin: (User) -> Boolean = { it.isAdmin }
val isNamedAlex: (User) -> Boolean = { it.name == "Alex" }
 
// Custom combinators (AND/OR)
infix fun <T> ((T) -> Boolean).and(other: (T) -> Boolean): (T) -> Boolean = { t ->
    this(t) && other(t)
}
 
infix fun <T> ((T) -> Boolean).or(other: (T) -> Boolean): (T) -> Boolean = { t ->
    this(t) || other(t)
}
 
fun main() {
    val users = listOf(
        User("Alex", 25, true),
        User("Bob", 17, false),
        User("Eve", 30, true)
    )
 
    // Compose predicates
    val isAdultAdmin = isAdult and isAdmin
    val isAlexOrAdmin = isNamedAlex or isAdmin
 
    val adultAdmins = users.filter(isAdultAdmin)
    println(adultAdmins) // [User(name=Alex, age=25, isAdmin=true), User(name=Eve, age=30, isAdmin=true)]
 
    val alexOrAdmins = users.filter(isAlexOrAdmin)
    println(alexOrAdmins) // [User(name=Alex, ...), User(name=Eve, ...)]
}

2.4 Kotlin Standard Library FP Tools

Core Concepts Explained

Kotlin’s standard library includes powerful FP operations for collections:

  1. filter: Select elements matching a condition.
  2. map: Transform elements (e.g., extract a property).
  3. groupBy: Create lookup maps by a key.
  4. fold/reduce: Aggregate values (e.g., sum, max).

Complete Code Example

data class Order(val id: Int, val amount: Double, val customer: String)
 
fun main() {
    val orders = listOf(
        Order(1, 100.0, "Alice"),
        Order(2, 200.0, "Bob"),
        Order(3, 150.0, "Alice")
    )
 
    // Filter: Orders over $150
    val largeOrders = orders.filter { it.amount > 150 }
    println(largeOrders) // [Order(id=2, amount=200.0, customer=Bob)]
 
    // Map: Extract amounts
    val amounts = orders.map { it.amount }
    println(amounts) // [100.0, 200.0, 150.0]
 
    // GroupBy: Orders per customer
    val ordersByCustomer = orders.groupBy { it.customer }
    println(ordersByCustomer["Alice"]) // [Order(id=1, ...), Order(id=3, ...)]
 
    // Reduce: Total amount of all orders
    val total = orders.map { it.amount }.reduce { acc, amount -> acc + amount }
    println(total) // 450.0
 
    // SumOf (idiomatic alternative)
    val totalAlt = orders.sumOf { it.amount.toInt() } // For demonstration
    println(totalAlt) // 450
}

Key Takeaways

ConceptWhy It MattersExample
LambdasEnable concise, inline logic{ it.price < 100 }
Extension FunctionsExtend classes without inheritancefun List<Product>.findByName()
Function CompositionBuild complex logic from simple partsisAdult and isAdmin
groupBy/mapTransform and organize data efficientlyorders.groupBy { it.customer }

Section 3: Object-Oriented Kotlin

Detailed Explanations with Complete Code Examples


3.1 Classes & Objects

Core Concepts Explained

Kotlin’s OOP model enhances Java’s with concise syntax and modern features:

  1. Primary Constructor: Declared directly in the class header.
  2. Secondary Constructors: Added with constructor keyword (less common).
  3. init Blocks: Code executed during object initialization.
  4. Properties: Combine fields and accessors (get/set).

Complete Code Example

// Primary constructor with properties
class Person(val name: String, var age: Int) { 
    // Secondary constructor (delegates to primary)
    constructor(name: String) : this(name, 0)
    
    // init block runs at object creation
    init { 
        println("Person $name created")
    }
    
    // Method
    fun greet() = println("Hello, I'm $name")
    
    // Custom property (no backing field)
    val isAdult: Boolean
        get() = age >= 18
}
 
fun main() {
    val alice = Person("Alice", 29) // Prints "Person Alice created"
    alice.greet() // "Hello, I'm Alice"
    println(alice.isAdult) // true
    
    val bob = Person("Bob") // Secondary constructor
    println(bob.age) // 0 (default)
}

3.2 Data Classes & Destructuring

Core Concepts Explained

Data classes (data class) automatically generate:

  1. equals()/hashCode(): Value-based equality.
  2. toString(): Human-readable string.
  3. copy(): Create modified copies.
  4. Destructuring: Break objects into variables.

Complete Code Example

data class Book(val title: String, val author: String, val year: Int)
 
fun main() {
    val book = Book("Dune", "Frank Herbert", 1965)
    
    // toString() auto-generated
    println(book) // Book(title=Dune, author=Frank Herbert, year=1965)
    
    // copy() with modified values
    val updatedBook = book.copy(year = 2021)
    println(updatedBook) // Book(title=Dune, ..., year=2021)
    
    // Destructuring
    val (title, author, year) = book
    println("$title by $author ($year)") // Dune by Frank Herbert (1965)
    
    // Equality check
    val book2 = Book("Dune", "Frank Herbert", 1965)
    println(book == book2) // true (values match)
}

3.3 Sealed Classes & Enums

Core Concepts Explained

  1. Sealed Classes:
    • Define restricted hierarchies (subclasses in the same file).
    • Exhaustive when checks (no else needed).
  2. Enums:
    • Fixed set of constants.
    • Can include properties and methods.

Complete Code Example

// Sealed class for payment methods
sealed class PaymentMethod {
    data class CreditCard(val number: String, val expiry: String) : PaymentMethod()
    data class PayPal(val email: String) : PaymentMethod()
    object Cash : PaymentMethod()
}
 
// Enum for fixed categories
enum class Category(val displayName: String) {
    ELECTRONICS("Electronics"),
    BOOKS("Books"),
    CLOTHING("Apparel")
}
 
fun processPayment(payment: PaymentMethod) {
    when (payment) { // Exhaustive check (no else needed)
        is PaymentMethod.CreditCard -> println("Processing card: ${payment.number}")
        is PaymentMethod.PayPal -> println("Processing PayPal: ${payment.email}")
        PaymentMethod.Cash -> println("Cash accepted")
    }
}
 
fun main() {
    val payment = PaymentMethod.CreditCard("1234-5678", "12/25")
    processPayment(payment) // "Processing card: 1234-5678"
    
    // Enum example
    println(Category.BOOKS.displayName) // "Books"
}

3.4 Inheritance & Interfaces

Core Concepts Explained

  1. Inheritance:
    • Classes are final by default (use open to allow inheritance).
    • Override methods with override.
  2. Interfaces:
    • Define contracts (can include default implementations).
    • Use by for delegation.

Complete Code Example

// Base class (open for inheritance)
open class Animal(val name: String) {
    open fun speak() = println("$name makes a sound")
}
 
// Subclass overriding method
class Dog(name: String) : Animal(name) {
    override fun speak() = println("$name barks!")
}
 
// Interface with default implementation
interface Logger {
    fun log(message: String) = println("LOG: $message")
}
 
// Class implementing interface via delegation
class ConsoleLogger : Logger
 
class UserService(private val logger: Logger) : Logger by logger {
    fun createUser(name: String) {
        logger.log("User $name created")
    }
}
 
fun main() {
    val dog = Dog("Buddy")
    dog.speak() // "Buddy barks!"
    
    val userService = UserService(ConsoleLogger())
    userService.createUser("Alice") // "LOG: User Alice created"
}

Key Takeaways

ConceptWhy It MattersExample
Data ClassesAuto-generated boilerplate codedata class Book(...)
Sealed ClassesExhaustive type checkssealed class PaymentMethod
open/overrideControlled inheritanceopen class Animal; override fun speak()
Interface DelegationReuse implementationsclass UserService : Logger by logger

Section 4: Advanced Kotlin

Detailed Explanations with Complete Code Examples


4.1 Coroutines & Asynchronous Programming

Core Concepts Explained

Coroutines simplify asynchronous code by managing threads and concurrency:

  1. Suspend Functions: Marked with suspend, can pause/resume execution without blocking threads.
  2. Coroutine Builders: launch (fire-and-forget), async (returns a result), runBlocking (bridges blocking code).
  3. Dispatchers: Define threads for execution (e.g., Dispatchers.IO for network calls).

Complete Code Example

import kotlinx.coroutines.*
 
suspend fun fetchData(): String {
    delay(1000) // Simulate network delay
    return "Data fetched"
}
 
fun main() = runBlocking { // Creates a coroutine scope
    println("Start: ${Thread.currentThread().name}")
    
    // Launch a coroutine (fire-and-forget)
    val job = launch(Dispatchers.IO) {
        println("Fetching data: ${Thread.currentThread().name}")
        val result = fetchData()
        println(result)
    }
    
    // Async/await for results
    val deferred = async(Dispatchers.Default) {
        (1..10).sum()
    }
    println("Sum: ${deferred.await()}")
    
    job.join() // Wait for coroutine to finish
    println("End")
}

Output:

Start: main
Sum: 55
Fetching data: DefaultDispatcher-worker-1
Data fetched
End

4.2 Type-Safe Builders & DSLs

Core Concepts Explained

Kotlin’s DSLs (Domain-Specific Languages) use nested lambdas and extension functions:

  1. @DslMarker: Restricts implicit receivers in nested blocks.
  2. Receiver Types: Lambdas with receivers (ClassName.() -> Unit) enable DSL syntax.

Complete Code Example

@DslMarker
annotation class HtmlDsl
 
class Html {
    val elements = mutableListOf<String>()
    
    fun body(block: Body.() -> Unit) {
        elements.add("<body>")
        elements.addAll(Body().apply(block).elements)
        elements.add("</body>")
    }
}
 
@HtmlDsl
class Body {
    val elements = mutableListOf<String>()
    
    fun h1(text: String) {
        elements.add("<h1>$text</h1>")
    }
    
    fun p(text: String) {
        elements.add("<p>$text</p>")
    }
}
 
fun html(block: Html.() -> Unit): Html {
    return Html().apply(block)
}
 
fun main() {
    val page = html {
        body {
            h1("Welcome")
            p("Hello, Kotlin DSL!")
        }
    }
    println(page.elements.joinToString("\n"))
}

Output:

<body>
<h1>Welcome</h1>
<p>Hello, Kotlin DSL!</p>
</body>

4.3 Delegated Properties

Core Concepts Explained

Delegation offloads property logic to helper classes:

  1. by lazy: Initializes on first access.
  2. Observable: Triggers callbacks on value changes.
  3. Custom Delegates: Implement ReadWriteProperty/ReadOnlyProperty.

Complete Code Example

import kotlin.properties.Delegates
 
class User {
    // Lazy initialization
    val bio by lazy { 
        println("Fetching bio...")
        "Kotlin Developer"
    }
    
    // Observable property
    var name: String by Delegates.observable("Anonymous") { 
        _, old, new -> println("$old$new")
    }
}
 
// Custom delegate for uppercase strings
class UppercaseDelegate : ReadWriteProperty<Any?, String> {
    private var value = ""
    
    override fun getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): String {
        return value
    }
    
    override fun setValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>, value: String) {
        this.value = value.uppercase()
    }
}
 
fun main() {
    val user = User()
    println(user.bio) // "Fetching bio..." then "Kotlin Developer"
    
    user.name = "alice" // Prints "Anonymous → ALICE"
    println(user.name) // "ALICE"
    
    var customName by UppercaseDelegate()
    customName = "bob"
    println(customName) // "BOB"
}

4.4 Reflection & Annotations

Core Concepts Explained

Reflection inspects code at runtime; annotations add metadata:

  1. KClass: Runtime representation of classes.
  2. Annotations: Define metadata with @Target, @Retention.

Complete Code Example

import kotlin.reflect.full.*
 
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Serializable
 
@Serializable
data class Person(val name: String, val age: Int)
 
fun serialize(obj: Any): String {
    val klass = obj::class
    if (klass.findAnnotation<Serializable>() == null) {
        throw IllegalArgumentException("Class not serializable")
    }
    return klass.declaredMemberProperties.joinToString(", ") { prop ->
        "${prop.name}=${prop.getter.call(obj)}"
    }
}
 
fun main() {
    val person = Person("Alice", 29)
    println(serialize(person)) // "name=Alice, age=29"
    
    val result = runCatching { serialize("NotSerializable") }
    println(result.exceptionOrNull()) // IllegalArgumentException
}

4.5 Kotlin Multiplatform (KMP)

Core Concepts Explained

Share code across platforms (JVM, JS, Native):

  1. Common Module: Platform-agnostic code.
  2. Platform-Specific Code: Use expect/actual declarations.

Complete Code Example

// Common module
expect fun currentTime(): String
 
class Greeter {
    fun greet() = "Hello! Time is ${currentTime()}"
}
 
// JVM implementation
actual fun currentTime(): String {
    return java.time.LocalDateTime.now().toString()
}
 
// JavaScript implementation (hypothetical)
// actual fun currentTime(): String = Date().toString()

Key Takeaways

ConceptWhy It MattersExample
CoroutinesNon-blocking async codelaunch(Dispatchers.IO) { ... }
DSLsDomain-specific syntaxhtml { body { ... } }
DelegatesReusable property logicvar name by Delegates.observable(...)
AnnotationsMetadata for reflection@Serializable class Person

Section 5: Practical Kotlin

Real-World Applications and Best Practices


5.1 Unit Testing

Core Concepts Explained

Testing is critical for robust code. Kotlin works seamlessly with:

  1. JUnit 5: Modern testing framework.
  2. MockK: Mocking library for Kotlin (better than Mockito for coroutines/DSL support).
  3. Coroutine Testing: Use runTest to handle async code.

Complete Code Example

import io.mockk.*
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
 
class UserService(private val userRepository: UserRepository) {
    suspend fun getUserName(id: Int): String? {
        return userRepository.getUser(id)?.name
    }
}
 
interface UserRepository {
    suspend fun getUser(id: Int): User?
}
 
data class User(val id: Int, val name: String)
 
class UserServiceTest {
    private val userRepository = mockk<UserRepository>()
    private val userService = UserService(userRepository)
 
    @Test
    fun `getUserName returns name when user exists`() = runBlocking {
        // Stub the repository response
        coEvery { userRepository.getUser(1) } returns User(1, "Alice")
        
        val result = userService.getUserName(1)
        assertEquals("Alice", result)
        
        // Verify interaction
        coVerify { userRepository.getUser(1) }
    }
 
    @Test
    fun `getUserName returns null for invalid user`() = runBlocking {
        coEvery { userRepository.getUser(2) } returns null
        assertEquals(null, userService.getUserName(2))
    }
}

5.2 Android-Specific Kotlin

Core Concepts Explained

Kotlin is the preferred language for Android, with modern tools:

  1. Jetpack Compose: Declarative UI framework.
  2. ViewModel: Manages UI-related data.
  3. StateFlow: Observables for state management.

Complete Code Example

// ViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
 
class CounterViewModel : ViewModel() {
    private val _count = MutableStateFlow(0)
    val count: StateFlow<Int> = _count.asStateFlow()
 
    fun increment() {
        _count.value++
    }
}
 
// Composable UI
@Composable
fun CounterScreen(viewModel: CounterViewModel) {
    val count by viewModel.count.collectAsState()
    
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = "Count: $count")
        Button(onClick = viewModel::increment) {
            Text("Increment")
        }
    }
}

5.3 Spring Boot with Kotlin

Core Concepts Explained

Kotlin simplifies Spring Boot development with:

  1. Null-Safe APIs: Reduce runtime errors.
  2. Coroutine Support: Non-blocking endpoints.
  3. Kofu DSL: Functional bean configuration.

Complete Code Example

@RestController
class GreetingController {
    @GetMapping("/greeting")
    suspend fun greeting(@RequestParam name: String?) = coroutineScope {
        val userName = name ?: "Guest"
        mapOf("message" to "Hello, $userName!")
    }
}
 
// Application.kt
@SpringBootApplication
class Application {
    @Bean
    fun router() = router {
        GET("/api/hello") { ok().body("Hello from Kotlin!") }
    }
}
 
fun main(args: Array<String>) {
    runApplication<Application>(*args)
}

5.4 Best Practices & Idioms

Core Concepts Explained

Write idiomatic Kotlin with these patterns:

  1. Immutability: Prefer val over var.
  2. Expression-Bodied Functions: Use = ... for simple functions.
  3. Scope Functions: apply, also, let, run, with.

Complete Code Example

data class Config(var host: String = "", var port: Int = 0)
 
fun main() {
    // apply: Initialize objects
    val config = Config().apply {
        host = "localhost"
        port = 8080
    }
    
    // let: Null-safe transformation
    val nullableString: String? = "Kotlin"
    val length = nullableString?.let { 
        it.length  // Only executed if non-null
    } ?: 0
    
    // Idiomatic data processing
    val numbers = listOf(1, 2, 3)
    val doubled = numbers
        .filter { it > 1 }
        .map { it * 2 }
    
    // Destructuring in lambdas
    val users = listOf(User(1, "Alice"), User(2, "Bob"))
    users.forEach { (id, name) -> println("$id: $name") }
}

Key Takeaways

ConceptWhy It MattersExample
MockKKotlin-friendly mockingcoEvery { ... } returns ...
Jetpack ComposeDeclarative Android UIText("Count: $count")
Spring CoroutinesNon-blocking APIssuspend fun greeting()
Scope FunctionsConcise object handlingconfig.apply { ... }