Communitygithub.com

Kotlin 開發模式

符合慣用法的 Kotlin 模式、最佳實踐與慣例,運用協程、空值安全與 DSL 建構器,打造穩健、高效且易於維護的 Kotlin 應用程式。適用於編寫或審查 Kotlin 程式碼,以及處理慣用結構或空值安全問題時。

Kotlin 開發模式 是什麼?

「Kotlin 開發模式」技能專為在使用 Claude Code、Cursor 或 Codex 等 AI 代理時,強制執行符合慣用法的 Kotlin 編碼慣例而設計。該技能涵蓋七個關鍵領域:透過型別系統與安全呼叫運算子實現的空值安全;使用 `val` 與資料類別的 `copy()` 實現的不變性;用於窮舉型別階層的密封類別與介面;透過協程與 `Flow` 實現的結構化並行;用於在不使用繼承的情況下新增行為的擴充函式;使用 `@DslMarker` 與 lambda 接收器實現的型別安全 DSL 建構器;以及用於建置配置的 Gradle Kotlin DSL。此技能內含大量範例,展示正確與不正確的 Kotlin 用法,提供關於資料類別、作用域函式、委派、惰性求值(Sequence)及取消處理的明確指引。對於習慣 Java 的開發者或任何希望在 Kotlin 專案中撰寫更簡潔、更安全且更具表達力程式碼的人來說,此技能極具價值。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/kotlin-patterns

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

Kotlin 開發模式 是做什麼的?

符合慣用法的 Kotlin 模式與最佳實踐,用於打造穩健、高效且易於維護的應用程式。

使用時機

  • 編寫新的 Kotlin 程式碼
  • 審查 Kotlin 程式碼
  • 重構現有的 Kotlin 程式碼
  • 設計 Kotlin 模組或函式庫
  • 配置 Gradle Kotlin DSL 建置

運作方式

此技能在七個關鍵領域強制執行符合慣用法的 Kotlin 慣例:使用型別系統與安全呼叫運算子的空值安全;透過 val 與資料類別的 copy() 實現的不變性;用於窮舉型別階層的密封類別與介面;使用協程與 Flow 實現的結構化並行;用於在不使用繼承的情況下新增行為的擴充函式;使用 @DslMarker 與 lambda 接收器實現的型別安全 DSL 建構器;以及用於建置配置的 Gradle Kotlin DSL。

範例

使用 Elvis 運算子的空值安全:

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

用於窮舉結果的密封類別:

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 的結構化並行:

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 的型別系統區分了可空與不可空型別。充分利用它。

// 良好:預設使用不可空型別
fun getUser(id: String): User {
    return userRepository.findById(id)
        ?: throw UserNotFoundException("User $id not found")
}

// 良好:安全呼叫與 Elvis 運算子
fun getUserEmail(userId: String): String {
    val user = userRepository.findById(userId)
    return user?.email ?: "[email protected]"
}

// 不良:強制解開可空型別
fun getUserEmail(userId: String): String {
    val user = userRepository.findById(userId)
    return user!!.email // 若為 null 則拋出 NPE
}

2. 預設不變性

偏好使用 val 而非 var,使用不可變集合而非可變集合。

// 良好:不可變資料
data class User(
    val id: String,
    val name: String,
    val email: String,
)

// 良好:使用 copy() 進行轉換
fun updateEmail(user: User, newEmail: String): User =
    user.copy(email = newEmail)

// 良好:不可變集合
val users: List<User> = listOf(user1, user2)
val filtered = users.filter { it.email.isNotBlank() }

// 不良:可變狀態
var currentUser: User? = null // 避免可變的全域狀態
val mutableUsers = mutableListOf<User>() // 除非真的需要,否則避免

3. 表達式主體與單表達式函式

使用表達式主體來撰寫簡潔、可讀的函式。

// 良好:表達式主體
fun isAdult(age: Int): Boolean = age >= 18

fun formatFullName(first: String, last: String): String =
    "$first $last".trim()

fun User.displayName(): String =
    name.ifBlank { email.substringBefore('@') }

// 良好:將 when 作為表達式
fun statusMessage(code: Int): String = when (code) {
    200 -> "OK"
    404 -> "Not Found"
    500 -> "Internal Server Error"
    else -> "Unknown status: $code"
}

// 不良:不必要的區塊主體
fun isAdult(age: Int): Boolean {
    return age >= 18
}

4. 用於值物件的資料類別

對於主要保存資料的型別,使用資料類別。

// 良好:附帶 copy、equals、hashCode、toString 的資料類別
data class CreateUserRequest(
    val name: String,
    val email: String,
    val role: Role = Role.USER,
)

// 良好:用於型別安全的值類別(執行時期零開銷)
@JvmInline
value class UserId(val value: String) {
    init {
        require(value.isNotBlank()) { "UserId 不能為空白" }
    }
}

@JvmInline
value class Email(val value: String) {
    init {
        require('@' in value) { "無效的電子郵件:$value" }
    }
}

fun getUser(id: UserId): User = userRepository.findById(id)

密封類別與介面

對受限階層建模

// 良好:用於窮舉 when 的密封類別
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>()
}

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("仍在載入中")
}

用於 API 回應的密封介面

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
}

作用域函式

何時使用哪一個

// let:轉換可空值或作用域結果
val length: Int? = name?.let { it.trim().length }

// apply:配置物件(回傳該物件)
val user = User().apply {
    name = "Alice"
    email = "[email protected]"
}

// also:副作用(回傳該物件)
val user = createUser(request).also { logger.info("已建立使用者:${it.id}") }

// run:以接收器執行區塊(回傳結果)
val result = connection.run {
    prepareStatement(sql)
    executeQuery()
}

// with:run 的非擴充形式
val csv = with(StringBuilder()) {
    appendLine("name,email")
    users.forEach { appendLine("${it.name},${it.email}") }
    toString()
}

反模式

// 不良:巢狀作用域函式
user?.let { u ->
    u.address?.let { addr ->
        addr.city?.let { city ->
            println(city) // 難以閱讀
        }
    }
}

// 良好:改為鏈式安全呼叫
val city = user?.address?.city
city?.let { println(it) }

擴充函式

在不使用繼承的情況下新增功能

// 良好:領域特定的擴充
fun 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()

// 良好:集合擴充
fun <T> List<T>.second(): T = this[1]

fun <T> List<T>.secondOrNull(): T? = getOrNull(1)

// 良好:作用域擴充(不污染全域命名空間)
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() }
}

協程

結構化並行

// 良好:使用 coroutineScope 的結構化並行
suspend 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(),
        )
    }

// 良好:當子任務可以獨立失敗時使用 supervisorScope
suspend 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

// 良好:具有適當錯誤處理的冷 flow
fun observeUsers(): Flow<List<User>> = flow {
    while (currentCoroutineContext().isActive) {
        val users = userRepository.findAll()
        emit(users)
        delay(5.seconds)
    }
}.catch { e ->
    logger.error("觀察使用者時出錯", e)
    emit(emptyList())
}

// 良好:Flow 運算子
fun searchUsers(query: Flow<String>): Flow<List<User>> =
    query
        .debounce(300.milliseconds)
        .distinctUntilChanged()
        .filter { it.length >= 2 }
        .mapLatest { q -> userRepository.search(q) }
        .catch { emit(emptyList()) }

取消與清理

// 良好:尊重取消
suspend fun processItems(items: List<Item>) {
    items.forEach { item ->
        ensureActive() // 在執行昂貴工作前檢查取消
        processItem(item)
    }
}

// 良好:使用 try/finally 進行清理
suspend fun acquireAndProcess() {
    val resource = acquireResource()
    try {
        resource.process()
    } finally {
        withContext(NonCancellable) {
            resource.release() // 即使取消也始終釋放
        }
    }
}

委派

屬性委派

// 惰性初始化
val expensiveData: List<User> by lazy {
    userRepository.findAll()
}

// 可觀察屬性
var name: String by Delegates.observable("initial") { _, old, new ->
    logger.info("名稱從 '$old' 變更為 '$new'")
}

// 基於 Map 的屬性
class 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))

介面委派

// 良好:委派介面實作
class LoggingUserRepository(
    private val delegate: UserRepository,
    private val logger: Logger,
) : UserRepository by delegate {
    // 僅覆蓋需要新增日誌記錄的部分
    override suspend fun findById(id: String): User? {
        logger.info(\

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

相關技能