kotlin-guide
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseKotlin Guide
Kotlin 开发指南
Applies to: Kotlin 1.9+, JVM 17+, Coroutines, Android, Server-side
适用范围:Kotlin 1.9+、JVM 17+、协程、Android、服务端
Core Principles
核心原则
- Null Safety: Leverage the type system to eliminate null pointer exceptions at compile time
- Conciseness: Use data classes, scope functions, and destructuring to reduce boilerplate
- Coroutines for Concurrency: Structured concurrency with coroutines, not raw threads
- Immutability by Default: Prefer over
val, immutable collections over mutablevar - Interop Awareness: Write Kotlin-idiomatic code while maintaining clean Java interop boundaries
- 空安全:利用类型系统在编译期消除空指针异常
- 简洁性:使用数据类、作用域函数、解构来减少样板代码
- 协程实现并发:使用协程实现结构化并发,而非原始线程
- 默认不可变:优先使用而非
val,优先使用不可变集合而非可变集合var - 互操作性感知:编写符合Kotlin惯用风格的代码,同时保持清晰的Java互操作边界
Guardrails
防护规则
Version & Dependencies
版本与依赖
- Use Kotlin 1.9+ with Gradle Kotlin DSL ()
build.gradle.kts - Target JVM 17+ for server-side, match Android minSdk for mobile
- Pin Kotlin and kotlinx library versions together (BOM alignment)
- Run to audit transitive dependencies
./gradlew dependencies
- 使用Kotlin 1.9+搭配Gradle Kotlin DSL()
build.gradle.kts - 服务端目标版本为JVM 17+,移动端匹配Android minSdk要求
- 统一锁定Kotlin与kotlinx库版本(BOM对齐)
- 执行审计传递依赖
./gradlew dependencies
Code Style
代码风格
- Run before every commit (format + check)
ktlint - Run for static analysis (complexity, code smells)
detekt - Packages: lowercase, no underscores ()
com.example.userservice - Classes: | Functions/properties:
PascalCase| Constants:camelCaseSCREAMING_SNAKE_CASE - Follow Kotlin Coding Conventions
- 每次提交前执行(格式化+检查)
ktlint - 执行做静态分析(复杂度检查、代码异味排查)
detekt - 包名:全小写,无下划线(例如)
com.example.userservice - 类名:| 函数/属性名:
PascalCase| 常量名:camelCaseSCREAMING_SNAKE_CASE - 遵循Kotlin官方编码规范
Null Safety
空安全
- Never use (not-null assertion) in production code
!! - Use safe calls (), elvis (
?.), and smart casts instead?: - Nullable types only at API boundaries (deserialization, Java interop)
- Prefer or
requireNotNull()with meaningful messages overcheckNotNull()!! - Use for nullable transformations, not nested
?.let { }checksif
kotlin
// BAD: crashes at runtime
val length = name!!.length
// GOOD: safe call with fallback
val length = name?.length ?: 0
// GOOD: explicit contract with clear error
val validName = requireNotNull(name) { "User name must not be null for ID=$id" }- 生产代码中严禁使用(非空断言)
!! - 优先使用安全调用()、Elvis运算符(
?.)、智能类型转换替代?: - 可空类型仅允许在API边界使用(反序列化、Java互操作场景)
- 优先使用带明确错误信息的或
requireNotNull()替代checkNotNull()!! - 可空类型转换使用,而非嵌套
?.let { }判断if
kotlin
// BAD: 运行时会崩溃
val length = name!!.length
// GOOD: 带兜底逻辑的安全调用
val length = name?.length ?: 0
// GOOD: 明确的契约与清晰的错误提示
val validName = requireNotNull(name) { "ID为$id的用户姓名不能为空" }Coroutines
协程
- Always use structured concurrency (,
coroutineScope)supervisorScope - Never use in production code
GlobalScope - Use for blocking I/O operations
withContext(Dispatchers.IO) - Always handle correctly (rethrow, never swallow)
CancellationException - Set timeouts with or
withTimeoutfor external callswithTimeoutOrNull
kotlin
// BAD: unstructured, leaks coroutines
GlobalScope.launch { fetchData() }
// GOOD: structured concurrency, respects parent lifecycle
coroutineScope {
val user = async { userService.getUser(id) }
val orders = async { orderService.getOrders(id) }
UserWithOrders(user.await(), orders.await())
}- 始终使用结构化并发(、
coroutineScope)supervisorScope - 生产代码中严禁使用
GlobalScope - 阻塞I/O操作使用执行
withContext(Dispatchers.IO) - 必须正确处理(重新抛出,禁止吞掉异常)
CancellationException - 外部调用需使用或
withTimeout设置超时withTimeoutOrNull
kotlin
// BAD: 非结构化并发,会导致协程泄漏
GlobalScope.launch { fetchData() }
// GOOD: 结构化并发,遵循父级生命周期
coroutineScope {
val user = async { userService.getUser(id) }
val orders = async { orderService.getOrders(id) }
UserWithOrders(user.await(), orders.await())
}Extension Functions
扩展函数
- Use extension functions to add behavior, not to bypass access control
- Keep extensions in a dedicated file (,
StringExtensions.kt)DateExtensions.kt - Do not add extensions to or overly generic types
Any - Prefer member functions for core behavior, extensions for utility/convenience
- Document extensions with KDoc tag when purpose is not obvious
@receiver
- 使用扩展函数添加额外行为,不得用于绕过访问控制
- 扩展函数统一放在专用文件中(例如、
StringExtensions.kt)DateExtensions.kt - 禁止为或过于通用的类型添加扩展函数
Any - 核心行为优先使用成员函数,扩展函数仅用于工具类/便捷功能
- 当扩展函数用途不明确时,使用KDoc标签标注接收者
@receiver
Project Structure
项目结构
myproject/
├── app/ # Application module or main entry
│ └── src/main/kotlin/
├── domain/ # Business logic, entities, use cases
│ └── src/main/kotlin/
│ └── com/example/domain/
│ ├── model/ # Data classes, sealed classes
│ ├── repository/ # Repository interfaces
│ └── usecase/ # Business operations
├── data/ # Data layer implementations
│ └── src/main/kotlin/
│ └── com/example/data/
│ ├── repository/ # Repository implementations
│ ├── remote/ # API clients, DTOs
│ └── local/ # Database, DAOs
├── presentation/ # UI or API controllers
├── build.gradle.kts
├── settings.gradle.kts
└── gradle.properties- has zero framework dependencies (pure Kotlin)
domain/ - depends on
data/, implements repository interfacesdomain/ - depends on
presentation/, never imports fromdomain/directlydata/ - No circular module dependencies
myproject/
├── app/ # 应用模块或主入口
│ └── src/main/kotlin/
├── domain/ # 业务逻辑、实体、用例
│ └── src/main/kotlin/
│ └── com/example/domain/
│ ├── model/ # 数据类、密封类
│ ├── repository/ # 仓库接口
│ └── usecase/ # 业务操作
├── data/ # 数据层实现
│ └── src/main/kotlin/
│ └── com/example/data/
│ ├── repository/ # 仓库实现
│ ├── remote/ # API客户端、DTO
│ └── local/ # 数据库、DAO
├── presentation/ # UI或API控制器
├── build.gradle.kts
├── settings.gradle.kts
└── gradle.properties- 无任何框架依赖(纯Kotlin代码)
domain/ - 依赖
data/,实现仓库接口domain/ - 依赖
presentation/,禁止直接导入domain/的内容data/ - 不允许出现循环模块依赖
Key Patterns
核心模式
Data Classes & Value Classes
数据类与值类
kotlin
data class User(
val id: UserId,
val email: Email,
val name: String,
val role: Role = Role.VIEWER,
) {
init {
require(name.isNotBlank()) { "User name must not be blank" }
}
}
// Value classes for type-safe IDs (zero runtime overhead)
@JvmInline
value class UserId(val value: String) {
init { require(value.isNotBlank()) { "UserId must not be blank" } }
}kotlin
data class User(
val id: UserId,
val email: Email,
val name: String,
val role: Role = Role.VIEWER,
) {
init {
require(name.isNotBlank()) { "用户姓名不能为空" }
}
}
// 类型安全ID的值类(零运行时开销)
@JvmInline
value class UserId(val value: String) {
init { require(value.isNotBlank()) { "UserId不能为空" } }
}Sealed Classes & Interfaces
密封类与接口
kotlin
sealed interface Result<out T> {
data class Success<T>(val data: T) : Result<T>
data class Failure(val error: AppError) : Result<Nothing>
}
sealed class AppError(val message: String) {
data class NotFound(val resource: String, val id: String) :
AppError("$resource with ID $id not found")
data class Validation(val field: String, val reason: String) :
AppError("Validation failed for $field: $reason")
data class Unauthorized(val detail: String = "Authentication required") :
AppError(detail)
}
// Exhaustive when expressions
fun <T> Result<T>.getOrThrow(): T = when (this) {
is Result.Success -> data
is Result.Failure -> throw error.toException()
}kotlin
sealed interface Result<out T> {
data class Success<T>(val data: T) : Result<T>
data class Failure(val error: AppError) : Result<Nothing>
}
sealed class AppError(val message: String) {
data class NotFound(val resource: String, val id: String) :
AppError("ID为$id的$resource不存在")
data class Validation(val field: String, val reason: String) :
AppError("$field校验失败: $reason")
data class Unauthorized(val detail: String = "需要身份认证") :
AppError(detail)
}
// 穷尽式when表达式
fun <T> Result<T>.getOrThrow(): T = when (this) {
is Result.Success -> data
is Result.Failure -> throw error.toException()
}Scope Functions Quick Reference
作用域函数速查
| Function | Context | Returns | Use for |
|---|---|---|---|
| | Lambda result | Nullable transforms, scoped vars |
| | Lambda result | Compute value using object context |
| | Lambda result | Operate on non-null object |
| | Object itself | Configure/build an object |
| | Object itself | Side effects (logging, events) |
kotlin
// apply: configure and return the object
val request = HttpRequest.Builder().apply {
url(endpoint)
header("Authorization", "Bearer $token")
timeout(Duration.ofSeconds(30))
}.build()
// also: side effects without modifying the chain
val savedUser = userRepository.save(newUser).also { user ->
logger.info("Created user: ${user.id}")
}| 函数 | 上下文 | 返回值 | 适用场景 |
|---|---|---|---|
| | Lambda返回值 | 可空类型转换、作用域变量 |
| | Lambda返回值 | 基于对象上下文计算值 |
| | Lambda返回值 | 操作非空对象 |
| | 对象本身 | 配置/构建对象 |
| | 对象本身 | 副作用操作(日志、事件) |
kotlin
// apply: 配置并返回对象
val request = HttpRequest.Builder().apply {
url(endpoint)
header("Authorization", "Bearer $token")
timeout(Duration.ofSeconds(30))
}.build()
// also: 不修改调用链的副作用操作
val savedUser = userRepository.save(newUser).also { user ->
logger.info("创建用户: ${user.id}")
}Testing
测试
Standards
标准
- Test files: in
*Test.kt(mirror source package)src/test/kotlin/ - Use JUnit 5 with ,
@Test,@Nested@DisplayName - Use MockK for mocking (idiomatic Kotlin, supports coroutines)
- Table-driven style with and
@ParameterizedTest@MethodSource - Coverage target: >80% for business logic, >60% overall
- Use from
runTestfor coroutine testskotlinx-coroutines-test
- 测试文件:放在
*Test.kt下(与源码包结构对应)src/test/kotlin/ - 使用JUnit 5的、
@Test、@Nested注解@DisplayName - 使用MockK做mock(符合Kotlin惯用风格,支持协程)
- 表驱动测试使用和
@ParameterizedTest实现@MethodSource - 覆盖率目标:业务逻辑>80%,整体>60%
- 协程测试使用提供的
kotlinx-coroutines-testrunTest
Unit Test Pattern
单元测试模式
kotlin
class UserServiceTest {
private val userRepository = mockk<UserRepository>()
private val eventBus = mockk<EventBus>(relaxed = true)
private val service = UserService(userRepository, eventBus)
@Nested
@DisplayName("createUser")
inner class CreateUser {
@Test
fun `creates user with valid input`() = runTest {
coEvery { userRepository.save(any()) } returns mockUser
val result = service.createUser(validInput)
assertThat(result).isInstanceOf(Result.Success::class.java)
coVerify { userRepository.save(any()) }
coVerify { eventBus.publish(any<UserCreatedEvent>()) }
}
@Test
fun `fails with blank name`() = runTest {
val result = service.createUser(blankNameInput)
assertThat(result).isInstanceOf(Result.Failure::class.java)
coVerify(exactly = 0) { userRepository.save(any()) }
}
}
}kotlin
class UserServiceTest {
private val userRepository = mockk<UserRepository>()
private val eventBus = mockk<EventBus>(relaxed = true)
private val service = UserService(userRepository, eventBus)
@Nested
@DisplayName("createUser方法")
inner class CreateUser {
@Test
fun `输入合法时创建用户成功`() = runTest {
coEvery { userRepository.save(any()) } returns mockUser
val result = service.createUser(validInput)
assertThat(result).isInstanceOf(Result.Success::class.java)
coVerify { userRepository.save(any()) }
coVerify { eventBus.publish(any<UserCreatedEvent>()) }
}
@Test
fun `姓名为空时创建失败`() = runTest {
val result = service.createUser(blankNameInput)
assertThat(result).isInstanceOf(Result.Failure::class.java)
coVerify(exactly = 0) { userRepository.save(any()) }
}
}
}Parameterized Tests
参数化测试
kotlin
companion object {
@JvmStatic
fun emailCases() = listOf(
Arguments.of("user@example.com", true),
Arguments.of("invalid-email", false),
Arguments.of("", false),
)
}
@ParameterizedTest(name = "email \"{0}\" valid={1}")
@MethodSource("emailCases")
fun `validates email format`(email: String, expected: Boolean) {
val result = runCatching { Email(email) }
assertThat(result.isSuccess).isEqualTo(expected)
}kotlin
companion object {
@JvmStatic
fun emailCases() = listOf(
Arguments.of("user@example.com", true),
Arguments.of("invalid-email", false),
Arguments.of("", false),
)
}
@ParameterizedTest(name = "邮箱\"{0}\"校验结果={1}")
@MethodSource("emailCases")
fun `校验邮箱格式`(email: String, expected: Boolean) {
val result = runCatching { Email(email) }
assertThat(result.isSuccess).isEqualTo(expected)
}Tooling
工具链
Essential Commands
常用命令
bash
./gradlew build # Compile + test + check
./gradlew test # Run all tests
./gradlew test --tests "*.UserServiceTest" # Specific test class
./gradlew koverReport # Coverage report
./gradlew ktlintCheck # Check formatting
./gradlew ktlintFormat # Auto-fix formatting
./gradlew detekt # Static analysis
./gradlew dependencies # Dependency treebash
./gradlew build # 编译 + 测试 + 检查
./gradlew test # 运行所有测试
./gradlew test --tests "*.UserServiceTest" # 运行指定测试类
./gradlew koverReport # 生成覆盖率报告
./gradlew ktlintCheck # 检查代码格式
./gradlew ktlintFormat # 自动修复代码格式
./gradlew detekt # 静态分析
./gradlew dependencies # 生成依赖树Gradle Kotlin DSL Configuration
Gradle Kotlin DSL配置
kotlin
// build.gradle.kts
plugins {
kotlin("jvm") version "1.9.22"
id("org.jlleitschuh.gradle.ktlint") version "12.1.0"
id("io.gitlab.arturbosch.detekt") version "1.23.4"
id("org.jetbrains.kotlinx.kover") version "0.7.5"
}
kotlin { jvmToolchain(17) }
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
testImplementation(kotlin("test"))
testImplementation("io.mockk:mockk:1.13.9")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.0")
testImplementation("org.assertj:assertj-core:3.25.1")
}
detekt {
config.setFrom("detekt.yml")
buildUponDefaultConfig = true
}
kover {
verify { rule { minBound(80) } }
}kotlin
// build.gradle.kts
plugins {
kotlin("jvm") version "1.9.22"
id("org.jlleitschuh.gradle.ktlint") version "12.1.0"
id("io.gitlab.arturbosch.detekt") version "1.23.4"
id("org.jetbrains.kotlinx.kover") version "0.7.5"
}
kotlin { jvmToolchain(17) }
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
testImplementation(kotlin("test"))
testImplementation("io.mockk:mockk:1.13.9")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.0")
testImplementation("org.assertj:assertj-core:3.25.1")
}
detekt {
config.setFrom("detekt.yml")
buildUponDefaultConfig = true
}
kover {
verify { rule { minBound(80) } }
}Detekt Configuration
Detekt配置
yaml
undefinedyaml
undefineddetekt.yml
detekt.yml
complexity:
LongMethod:
threshold: 50
CyclomaticComplexMethod:
threshold: 10
LargeClass:
threshold: 300
LongParameterList:
functionThreshold: 5
constructorThreshold: 8
style:
ForbiddenComment:
values:
- "TODO(?!\(#\d+\))" # TODOs require issue reference
MagicNumber:
ignoreNumbers: ["-1", "0", "1", "2"]
MaxLineLength:
maxLineLength: 120
potential-bugs:
UnsafeCast:
active: true
undefinedcomplexity:
LongMethod:
threshold: 50
CyclomaticComplexMethod:
threshold: 10
LargeClass:
threshold: 300
LongParameterList:
functionThreshold: 5
constructorThreshold: 8
style:
ForbiddenComment:
values:
- "TODO(?!\(#\d+\))" # TODO必须关联issue编号
MagicNumber:
ignoreNumbers: ["-1", "0", "1", "2"]
MaxLineLength:
maxLineLength: 120
potential-bugs:
UnsafeCast:
active: true
undefinedReferences
参考资料
For detailed patterns and examples, see:
- references/patterns.md -- Coroutine patterns, sealed class hierarchies, DSL builders, Flow patterns
如需查看详细模式与示例,请访问:
- references/patterns.md -- 协程模式、密封类层级、DSL构建器、Flow模式