Kotlin Patterns

affaan-m/ECC/docs/zh-CN/skills/kotlin-patterns

by affaan-mef648e01899ba3e8dc6371642deaaf64b4477775No license275K starsListed Oct 9, 2026Updated Oct 9, 2026Repository updated 4 days ago

惯用的Kotlin模式、最佳实践和约定,用于构建健壮、高效且可维护的Kotlin应用程序,包括协程、空安全和DSL构建器。

Instructions onlySoftware Development
AI-generated overview

Reference guidance on idiomatic Kotlin patterns, including null safety, immutability, sealed types, coroutines, DSLs and Gradle Kotlin DSL.

What it does
This skill supplies a written reference of idiomatic Kotlin conventions and best practices across seven areas: null safety, immutability, sealed classes and interfaces, coroutines and Flow, extension functions, type-safe DSL builders, and Gradle Kotlin DSL configuration. It presents code examples contrasting good and bad usage, plus a quick-reference table of idioms and a list of anti-patterns. It produces guidance and example code rather than executable artifacts.
When to use it
Use it when writing, reviewing or refactoring Kotlin code, when designing Kotlin modules or libraries, or when configuring Gradle Kotlin DSL builds. It is meant as a style and convention reference during Kotlin development work.
Requirements
No tools, packages or credentials are required. The skill is instructions only and ships no scripts; the examples reference Kotlin libraries such as coroutines, Ktor, Exposed, Koin, Kotest and Mockk, but nothing needs to be installed to read the guidance.

Kotlin 开发模式

适用于构建健壮、高效、可维护应用程序的惯用 Kotlin 模式与最佳实践。

使用时机

  • 编写新的 Kotlin 代码
  • 审查 Kotlin 代码
  • 重构现有的 Kotlin 代码
  • 设计 Kotlin 模块或库
  • 配置 Gradle Kotlin DSL 构建

工作原理

本技能在七个关键领域强制执行惯用的 Kotlin 约定:使用类型系统和安全调用运算符实现空安全;通过数据类的 val 和 copy() 实现不可变性;使用密封类和接口实现穷举类型层次结构;使用协程和 Flow 实现结构化并发;使用扩展函数在不使用继承的情况下添加行为;使用 @DslMarker 和 lambda 接收器构建类型安全的 DSL;以及使用 Gradle Kotlin DSL 进行构建配置。

示例

使用 Elvis 运算符实现空安全:

kotlin
fun getUserEmail(userId: String): String {    val user = userRepository.findById(userId)    return user?.email ?: "[email protected]"}

使用密封类处理穷举结果:

kotlin
sealed class Result<out T> {    data class Success<T>(val data: T) : Result<T>()    data class Failure(val error: AppError) : Result<Nothing>()    data object Loading : Result<Nothing>()}

使用 async/await 实现结构化并发:

kotlin
suspend fun fetchUserWithPosts(userId: String): UserProfile =    coroutineScope {        val user = async { userService.getUser(userId) }        val posts = async { postService.getUserPosts(userId) }        UserProfile(user = user.await(), posts = posts.await())    }

核心原则

1. 空安全

Kotlin 的类型系统区分可空和不可空类型。充分利用它。

kotlin
// Good: Use non-nullable types by defaultfun getUser(id: String): User {    return userRepository.findById(id)        ?: throw UserNotFoundException("User $id not found")}
// Good: Safe calls and Elvis operatorfun getUserEmail(userId: String): String {    val user = userRepository.findById(userId)    return user?.email ?: "[email protected]"}
// Bad: Force-unwrapping nullable typesfun getUserEmail(userId: String): String {    val user = userRepository.findById(userId)    return user!!.email // Throws NPE if null}

2. 默认不可变性

优先使用 val 而非 var,优先使用不可变集合而非可变集合。

kotlin
// Good: Immutable datadata class User(    val id: String,    val name: String,    val email: String,)
// Good: Transform with copy()fun updateEmail(user: User, newEmail: String): User =    user.copy(email = newEmail)
// Good: Immutable collectionsval users: List<User> = listOf(user1, user2)val filtered = users.filter { it.email.isNotBlank() }
// Bad: Mutable statevar currentUser: User? = null // Avoid mutable global stateval mutableUsers = mutableListOf<User>() // Avoid unless truly needed

3. 表达式体和单表达式函数

使用表达式体编写简洁、可读的函数。

kotlin
// Good: Expression bodyfun isAdult(age: Int): Boolean = age >= 18
fun formatFullName(first: String, last: String): String =    "$first $last".trim()
fun User.displayName(): String =    name.ifBlank { email.substringBefore('@') }
// Good: When as expressionfun statusMessage(code: Int): String = when (code) {    200 -> "OK"    404 -> "Not Found"    500 -> "Internal Server Error"    else -> "Unknown status: $code"}
// Bad: Unnecessary block bodyfun isAdult(age: Int): Boolean {    return age >= 18}

4. 数据类用于值对象

使用数据类表示主要包含数据的类型。

kotlin
// Good: Data class with copy, equals, hashCode, toStringdata class CreateUserRequest(    val name: String,    val email: String,    val role: Role = Role.USER,)
// Good: Value class for type safety (zero overhead at runtime)@JvmInlinevalue class UserId(val value: String) {    init {        require(value.isNotBlank()) { "UserId cannot be blank" }    }}
@JvmInlinevalue class Email(val value: String) {    init {        require('@' in value) { "Invalid email: $value" }    }}
fun getUser(id: UserId): User = userRepository.findById(id)

密封类和接口

建模受限的层次结构

kotlin
// Good: Sealed class for exhaustive whensealed class Result<out T> {    data class Success<T>(val data: T) : Result<T>()    data class Failure(val error: AppError) : Result<Nothing>()    data object Loading : Result<Nothing>()}
fun <T> Result<T>.getOrNull(): T? = when (this) {    is Result.Success -> data    is Result.Failure -> null    is Result.Loading -> null}
fun <T> Result<T>.getOrThrow(): T = when (this) {    is Result.Success -> data    is Result.Failure -> throw error.toException()    is Result.Loading -> throw IllegalStateException("Still loading")}

用于 API 响应的密封接口

kotlin
sealed interface ApiError {    val message: String
    data class NotFound(override val message: String) : ApiError    data class Unauthorized(override val message: String) : ApiError    data class Validation(        override val message: String,        val field: String,    ) : ApiError    data class Internal(        override val message: String,        val cause: Throwable? = null,    ) : ApiError}
fun ApiError.toStatusCode(): Int = when (this) {    is ApiError.NotFound -> 404    is ApiError.Unauthorized -> 401    is ApiError.Validation -> 422    is ApiError.Internal -> 500}

作用域函数

何时使用各个函数

kotlin
// let: Transform nullable or scoped resultval length: Int? = name?.let { it.trim().length }
// apply: Configure an object (returns the object)val user = User().apply {    name = "Alice"    email = "[email protected]"}
// also: Side effects (returns the object)val user = createUser(request).also { logger.info("Created user: ${it.id}") }
// run: Execute a block with receiver (returns result)val result = connection.run {    prepareStatement(sql)    executeQuery()}
// with: Non-extension form of runval csv = with(StringBuilder()) {    appendLine("name,email")    users.forEach { appendLine("${it.name},${it.email}") }    toString()}

反模式

kotlin
// Bad: Nesting scope functionsuser?.let { u ->    u.address?.let { addr ->        addr.city?.let { city ->            println(city) // Hard to read        }    }}
// Good: Chain safe calls insteadval city = user?.address?.citycity?.let { println(it) }

扩展函数

在不使用继承的情况下添加功能

kotlin
// Good: Domain-specific extensionsfun String.toSlug(): String =    lowercase()        .replace(Regex("[^a-z0-9\\s-]"), "")        .replace(Regex("\\s+"), "-")        .trim('-')
fun Instant.toLocalDate(zone: ZoneId = ZoneId.systemDefault()): LocalDate =    atZone(zone).toLocalDate()
// Good: Collection extensionsfun <T> List<T>.second(): T = this[1]
fun <T> List<T>.secondOrNull(): T? = getOrNull(1)
// Good: Scoped extensions (not polluting global namespace)class UserService {    private fun User.isActive(): Boolean =        status == Status.ACTIVE && lastLogin.isAfter(Instant.now().minus(30, ChronoUnit.DAYS))
    fun getActiveUsers(): List<User> = userRepository.findAll().filter { it.isActive() }}

协程

结构化并发

kotlin
// Good: Structured concurrency with coroutineScopesuspend fun fetchUserWithPosts(userId: String): UserProfile =    coroutineScope {        val userDeferred = async { userService.getUser(userId) }        val postsDeferred = async { postService.getUserPosts(userId) }
        UserProfile(            user = userDeferred.await(),            posts = postsDeferred.await(),        )    }
// Good: supervisorScope when children can fail independentlysuspend fun fetchDashboard(userId: String): Dashboard =    supervisorScope {        val user = async { userService.getUser(userId) }        val notifications = async { notificationService.getRecent(userId) }        val recommendations = async { recommendationService.getFor(userId) }
        Dashboard(            user = user.await(),            notifications = try {                notifications.await()            } catch (e: CancellationException) {                throw e            } catch (e: Exception) {                emptyList()            },            recommendations = try {                recommendations.await()            } catch (e: CancellationException) {                throw e            } catch (e: Exception) {                emptyList()            },        )    }

Flow 用于响应式流

kotlin
// Good: Cold flow with proper error handlingfun observeUsers(): Flow<List<User>> = flow {    while (currentCoroutineContext().isActive) {        val users = userRepository.findAll()        emit(users)        delay(5.seconds)    }}.catch { e ->    logger.error("Error observing users", e)    emit(emptyList())}
// Good: Flow operatorsfun searchUsers(query: Flow<String>): Flow<List<User>> =    query        .debounce(300.milliseconds)        .distinctUntilChanged()        .filter { it.length >= 2 }        .mapLatest { q -> userRepository.search(q) }        .catch { emit(emptyList()) }

取消与清理

kotlin
// Good: Respect cancellationsuspend fun processItems(items: List<Item>) {    items.forEach { item ->        ensureActive() // Check cancellation before expensive work        processItem(item)    }}
// Good: Cleanup with try/finallysuspend fun acquireAndProcess() {    val resource = acquireResource()    try {        resource.process()    } finally {        withContext(NonCancellable) {            resource.release() // Always release, even on cancellation        }    }}

委托

属性委托

kotlin
// Lazy initializationval expensiveData: List<User> by lazy {    userRepository.findAll()}
// Observable propertyvar name: String by Delegates.observable("initial") { _, old, new ->    logger.info("Name changed from '$old' to '$new'")}
// Map-backed propertiesclass Config(private val map: Map<String, Any?>) {    val host: String by map    val port: Int by map    val debug: Boolean by map}
val config = Config(mapOf("host" to "localhost", "port" to 8080, "debug" to true))

接口委托

kotlin
// Good: Delegate interface implementationclass LoggingUserRepository(    private val delegate: UserRepository,    private val logger: Logger,) : UserRepository by delegate {    // Only override what you need to add logging to    override suspend fun findById(id: String): User? {        logger.info("Finding user by id: $id")        return delegate.findById(id).also {            logger.info("Found user: ${it?.name ?: "null"}")        }    }}

DSL 构建器

类型安全构建器

kotlin
// Good: DSL with @DslMarker@DslMarkerannotation class HtmlDsl
@HtmlDslclass HTML {    private val children = mutableListOf<Element>()
    fun head(init: Head.() -> Unit) {        children += Head().apply(init)    }
    fun body(init: Body.() -> Unit) {        children += Body().apply(init)    }
    override fun toString(): String = children.joinToString("\n")}
fun html(init: HTML.() -> Unit): HTML = HTML().apply(init)
// Usageval page = html {    head { title("My Page") }    body {        h1("Welcome")        p("Hello, World!")    }}

配置 DSL

kotlin
data class ServerConfig(    val host: String = "0.0.0.0",    val port: Int = 8080,    val ssl: SslConfig? = null,    val database: DatabaseConfig? = null,)
data class SslConfig(val certPath: String, val keyPath: String)data class DatabaseConfig(val url: String, val maxPoolSize: Int = 10)
class ServerConfigBuilder {    var host: String = "0.0.0.0"    var port: Int = 8080    private var ssl: SslConfig? = null    private var database: DatabaseConfig? = null
    fun ssl(certPath: String, keyPath: String) {        ssl = SslConfig(certPath, keyPath)    }
    fun database(url: String, maxPoolSize: Int = 10) {        database = DatabaseConfig(url, maxPoolSize)    }
    fun build(): ServerConfig = ServerConfig(host, port, ssl, database)}
fun serverConfig(init: ServerConfigBuilder.() -> Unit): ServerConfig =    ServerConfigBuilder().apply(init).build()
// Usageval config = serverConfig {    host = "0.0.0.0"    port = 443    ssl("/certs/cert.pem", "/certs/key.pem")    database("jdbc:postgresql://localhost:5432/mydb", maxPoolSize = 20)}

用于惰性求值的序列

kotlin
// Good: Use sequences for large collections with multiple operationsval result = users.asSequence()    .filter { it.isActive }    .map { it.email }    .filter { it.endsWith("@company.com") }    .take(10)    .toList()
// Good: Generate infinite sequencesval fibonacci: Sequence<Long> = sequence {    var a = 0L    var b = 1L    while (true) {        yield(a)        val next = a + b        a = b        b = next    }}
val first20 = fibonacci.take(20).toList()

Gradle Kotlin DSL

build.gradle.kts 配置

kotlin
// Check for latest versions: https://kotlinlang.org/docs/releases.htmlplugins {    kotlin("jvm") version "2.3.10"    kotlin("plugin.serialization") version "2.3.10"    id("io.ktor.plugin") version "3.4.0"    id("org.jetbrains.kotlinx.kover") version "0.9.7"    id("io.gitlab.arturbosch.detekt") version "1.23.8"}
group = "com.example"version = "1.0.0"
kotlin {    jvmToolchain(21)}
dependencies {    // Ktor    implementation("io.ktor:ktor-server-core:3.4.0")    implementation("io.ktor:ktor-server-netty:3.4.0")    implementation("io.ktor:ktor-server-content-negotiation:3.4.0")    implementation("io.ktor:ktor-serialization-kotlinx-json:3.4.0")
    // Exposed    implementation("org.jetbrains.exposed:exposed-core:1.0.0")    implementation("org.jetbrains.exposed:exposed-dao:1.0.0")    implementation("org.jetbrains.exposed:exposed-jdbc:1.0.0")    implementation("org.jetbrains.exposed:exposed-kotlin-datetime:1.0.0")
    // Koin    implementation("io.insert-koin:koin-ktor:4.2.0")
    // Coroutines    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
    // Testing    testImplementation("io.kotest:kotest-runner-junit5:6.1.4")    testImplementation("io.kotest:kotest-assertions-core:6.1.4")    testImplementation("io.kotest:kotest-property:6.1.4")    testImplementation("io.mockk:mockk:1.14.9")    testImplementation("io.ktor:ktor-server-test-host:3.4.0")    testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.2")}
tasks.withType<Test> {    useJUnitPlatform()}
detekt {    config.setFrom(files("config/detekt/detekt.yml"))    buildUponDefaultConfig = true}

错误处理模式

用于领域操作的 Result 类型

kotlin
// Good: Use Kotlin's Result or a custom sealed classsuspend fun createUser(request: CreateUserRequest): Result<User> = runCatching {    require(request.name.isNotBlank()) { "Name cannot be blank" }    require('@' in request.email) { "Invalid email format" }
    val user = User(        id = UserId(UUID.randomUUID().toString()),        name = request.name,        email = Email(request.email),    )    userRepository.save(user)    user}
// Good: Chain resultsval displayName = createUser(request)    .map { it.name }    .getOrElse { "Unknown" }

require, check, error

kotlin
// Good: Preconditions with clear messagesfun withdraw(account: Account, amount: Money): Account {    require(amount.value > 0) { "Amount must be positive: $amount" }    check(account.balance >= amount) { "Insufficient balance: ${account.balance} < $amount" }
    return account.copy(balance = account.balance - amount)}

集合操作

惯用的集合处理

kotlin
// Good: Chained operationsval activeAdminEmails: List<String> = users    .filter { it.role == Role.ADMIN && it.isActive }    .sortedBy { it.name }    .map { it.email }
// Good: Grouping and aggregationval usersByRole: Map<Role, List<User>> = users.groupBy { it.role }
val oldestByRole: Map<Role, User?> = users.groupBy { it.role }    .mapValues { (_, users) -> users.minByOrNull { it.createdAt } }
// Good: Associate for map creationval usersById: Map<UserId, User> = users.associateBy { it.id }
// Good: Partition for splittingval (active, inactive) = users.partition { it.isActive }

快速参考:Kotlin 惯用法

惯用法描述
val 优于 var优先使用不可变变量
data class用于具有 equals/hashCode/copy 的值对象
sealed class/interface用于受限的类型层次结构
value class用于零开销的类型安全包装器
表达式 when穷举模式匹配
安全调用 ?.空安全的成员访问
Elvis ?:为可空类型提供默认值
let/apply/also/run/with用于编写简洁代码的作用域函数
扩展函数在不使用继承的情况下添加行为
copy()数据类上的不可变更新
require/check前置条件断言
协程 async/await结构化并发执行
Flow冷响应式流
sequence惰性求值
委托 by在不使用继承的情况下重用实现

应避免的反模式

kotlin
// Bad: Force-unwrapping nullable typesval name = user!!.name
// Bad: Platform type leakage from Javafun getLength(s: String) = s.length // Safefun getLength(s: String?) = s?.length ?: 0 // Handle nulls from Java
// Bad: Mutable data classesdata class MutableUser(var name: String, var email: String)
// Bad: Using exceptions for control flowtry {    val user = findUser(id)} catch (e: NotFoundException) {    // Don't use exceptions for expected cases}
// Good: Use nullable return or Resultval user: User? = findUserOrNull(id)
// Bad: Ignoring coroutine scopeGlobalScope.launch { /* Avoid GlobalScope */ }
// Good: Use structured concurrencycoroutineScope {    launch { /* Properly scoped */ }}
// Bad: Deeply nested scope functionsuser?.let { u ->    u.address?.let { a ->        a.city?.let { c -> process(c) }    }}
// Good: Direct null-safe chainuser?.address?.city?.let { process(it) }

请记住:Kotlin 代码应简洁但可读。利用类型系统确保安全,优先使用不可变性,并使用协程处理并发。如有疑问,让编译器帮助你。

Source and attribution

Source:affaan-m/ECCindocs/zh-CN/skills/kotlin-patternsat commitef648e0

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal