编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • 质量保障

  • 产品思考

  • 软实力

  • 开发流程

  • Git应用

  • 技术模版

  • 技术规范

    • 技术规范
    • C++编程代码规范指南
    • Java编程代码规范指南
    • JavaScript编程规范指南
    • TypeScript编程规范指南
    • Python编程代码规范指南
    • Go编程代码规范指南
    • Kotlin编程代码规范指南
      • 目录
      • 01.规范概述
        • 1.1 为何需要代码规范
        • 1.2 核心目标
        • 1.3 要求等级
        • 1.4 Kotlin版本
      • 02.命名规范
        • 2.1 命名总表
        • 2.2 正确与错误示例
        • 2.3 命名反模式
      • 03.代码格式规范
        • 3.1 缩进与空格 【必须】
        • 3.2 大括号与尾逗号 【必须】
        • 3.3 换行与对齐
        • 3.4 空行与分隔
      • 04.注释规范
        • 4.1 核心原则
        • 4.2 KDoc类注释
        • 4.3 函数注释
        • 4.4 TODO 与 FIXME
      • 05.类与对象设计规范
        • 5.1 data class 【必须】
        • 5.2 sealed class 与 sealed interface 【推荐】
        • 5.3 object 与 companion object 【推荐】
        • 5.4 可见性修饰符 【必须】
        • 5.5 继承与组合 【推荐】
      • 06.函数规范
        • 6.1 单表达式函数 【推荐】
        • 6.2 参数与默认值 【推荐】
        • 6.3 扩展函数与属性 【推荐】
        • 6.4 中缀与操作符 【推荐】
        • 6.5 Lambda 与高阶函数 【推荐】
      • 07.表达式与控制流
        • 7.1 when 表达式 【必须】
        • 7.2 if/try 作为表达式 【推荐】
        • 7.3 Elvis 与提前返回 【推荐】
        • 7.4 解构声明 【可选】
        • 7.5 智能类型转换
      • 08.空安全
        • 8.1 安全调用链 【必须】
        • 8.2 Elvis 操作符进阶 【必须】
        • 8.3 lateinit 与 lazy 【推荐】
        • 8.4 平台类型处理 【必须】
        • 8.5 !! 的使用边界 【必须】
      • 09.作用域函数
        • 9.1 函数速查表
        • 9.2 选型决策树
        • 9.3 典型场景示例
        • 9.4 嵌套与链式规则
      • 10.协程规范
        • 10.1 结构化并发 【必须】
        • 10.2 调度器选择 【必须】
        • 10.3 异常处理 【必须】
        • 10.4 取消与资源释放 【推荐】
        • 10.5 Flow 规范 【推荐】
      • 11.集合与序列
        • 11.1 不可变优先 【必须】
        • 11.2 函数式链式调用
        • 11.3 Sequence 序列 【可选】
        • 11.4 分组与聚合
      • 12.现代Kotlin特性
        • 12.1 类型别名(typealias)【可选】
        • 12.2 委托属性 【推荐】
        • 12.3 内联类与值类 【推荐】
        • 12.4 内联函数与 reified 【推荐】
        • 12.5 契约 Contracts 【可选】
      • 13.工具链与自动化
        • 13.1 ktlint格式化
        • 13.2 detekt静态分析
        • 13.3 CI 集成
        • 13.4 pre-commit 钩子 【推荐】
      • 14.常见反模式
      • 15.代码审查清单
      • 16.常见陷阱速查
        • 16.1 空安全陷阱
        • 16.2 协程陷阱
        • 16.3 集合陷阱
        • 16.4 类型与泛型陷阱
    • Swift编程代码规范指南
    • Rust编程代码规范指南
    • Shell编程代码规范指南
    • 项目代码提交规范
  • markdown

  • mermaid

  • license

  • 博客部署

  • 技术招聘

  • 测试经验

  • 技术
  • 技术规范
杨充
2020-12-01
目录

Kotlin编程代码规范指南

# Kotlin编程代码规范指南

本规范参考 Kotlin Coding Conventions (opens new window) 及 Android Kotlin Style Guide (opens new window),结合项目实践精简整理。

# 目录

  • 01.规范概述
    • 1.1 为何需要代码规范
    • 1.2 核心目标
    • 1.3 要求等级
    • 1.4 Kotlin版本
  • 02.命名规范
    • 2.1 命名总表
    • 2.2 正确与错误示例
    • 2.3 命名反模式
  • 03.代码格式规范
    • 3.1 缩进与空格
    • 3.2 大括号与尾逗号
    • 3.3 换行与对齐
    • 3.4 空行与分隔
  • 04.注释规范
    • 4.1 核心原则
    • 4.2 KDoc类注释
    • 4.3 函数注释
    • 4.4 TODO与FIXME
  • 05.类与对象设计规范
    • 5.1 data class
    • 5.2 sealed class与sealed interface
    • 5.3 object与companion object
    • 5.4 可见性修饰符
    • 5.5 继承与组合
  • 06.函数规范
    • 6.1 单表达式函数
    • 6.2 参数与默认值
    • 6.3 扩展函数与属性
    • 6.4 中缀与操作符
    • 6.5 Lambda与高阶函数
  • 07.表达式与控制流
    • 7.1 when表达式
    • 7.2 if/try作为表达式
    • 7.3 Elvis与提前返回
    • 7.4 解构声明
    • 7.5 智能类型转换
  • 08.空安全
    • 8.1 安全调用链
    • 8.2 Elvis操作符进阶
    • 8.3 lateinit与lazy
    • 8.4 平台类型处理
    • 8.5 !!的使用边界
  • 09.作用域函数
    • 9.1 函数速查表
    • 9.2 选型决策树
    • 9.3 典型场景示例
    • 9.4 嵌套与链式规则
  • 10.协程规范
    • 10.1 结构化并发
    • 10.2 调度器选择
    • 10.3 异常处理
    • 10.4 取消与资源释放
    • 10.5 Flow规范
  • 11.集合与序列
    • 11.1 不可变优先
    • 11.2 函数式链式调用
    • 11.3 Sequence序列
    • 11.4 分组与聚合
  • 12.现代Kotlin特性
    • 12.1 类型别名
    • 12.2 委托属性
    • 12.3 内联类与值类
    • 12.4 内联函数与reified
    • 12.5 契约Contracts
  • 13.工具链与自动化
    • 13.1 ktlint格式化
    • 13.2 detekt静态分析
    • 13.3 CI集成
    • 13.4 pre-commit钩子
  • 14.常见反模式
  • 15.代码审查清单
  • 16.常见陷阱速查
    • 16.1 空安全陷阱
    • 16.2 协程陷阱
    • 16.3 集合陷阱
    • 16.4 类型与泛型陷阱

# 01.规范概述

# 1.1 为何需要代码规范

疑惑:Kotlin 语法灵活,代码能跑就行,为什么还要花时间制定规范?

答疑:Kotlin 以简洁著称,但“简洁”不等于“随意”。同一行代码可以有多种写法——it 滥用、作用域函数嵌套、!! 断言、随处 GlobalScope——这些看似“方便”的写法在团队协作中会成为维护噩梦。规范的本质是用统一的写法写安全的代码。

# 1.2 核心目标

目标 说明
可读性 代码像 Kotlin 一样简洁优雅
一致性 整个项目像一个人写的
安全性 从编码层面避免 NPE、协程泄漏、集合误用
可维护性 半年后自己还能快速读懂
可审查性 Code Review 聚焦逻辑而非格式

# 1.3 要求等级

  • 必须(Mandatory):必须采用,违反将在 Code Review 中被驳回
  • 推荐(Preferable):理应采用,特殊情况可不采用但需注释说明
  • 可选(Optional):团队自行决定

# 1.4 Kotlin版本

代码应针对 Kotlin 1.9+,优先使用稳定特性,不使用实验性 API(除非标记 @OptIn 且经过充分讨论)。


# 02.命名规范

# 2.1 命名总表

类型 规则 示例
包名 全小写 + 点分隔 com.example.myapp.data
文件名 大驼峰(同主类名) UserRepository.kt, StringExt.kt
类/接口/对象 大驼峰 MainActivity, UserService, AppConfig
函数 小驼峰(动词开头) getUser(), fetchData(), isValid()
属性 小驼峰(名词) userName, isActive, maxRetryCount
常量(顶层/companion) 全大写 + 下划线 MAX_COUNT, DEFAULT_HOST
枚举常量 全大写 + 下划线 Color.RED, Status.SUCCESS
类型参数 单大写字母或大驼峰 + T T, ItemT, MapT
测试方法 反引号允许空格 fun `should return user when id exists`()
扩展函数/属性 小驼峰(同函数) fun String.isEmail()

# 2.2 正确与错误示例

// ✅ 正确
const val MAX_RETRY_COUNT = 3
class UserRepository(private val api: UserApi) {
    fun getUser(userId: Long): User? { ... }
    val isReady: Boolean get() = ...
}

// ✅ 返回布尔值的属性/函数用 is/has 前缀
val isVisible: Boolean get() = visibility == View.VISIBLE
fun hasPermission(perm: Permission): Boolean

// ❌ 错误
const val MaxRetryCount = 3          // 常量非全大写
class userRepository {}             // 类名未大驼峰
fun get_user(user_id: Long) {}      // 函数名用了下划线
var userName: String = ""           // 在 Kotlin 中函数/属性无天然区别 → 属性用名词
fun visible() = ...                 // 布尔返回值应 isVisible
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 2.3 命名反模式

反模式 示例 改进
过度缩写 usrRepo, btnClk userRepository, onButtonClick
含义不清 data, info, tmp userData, configInfo
否定命名 isNotEmpty() isEmpty() 后取反
拼音命名 dianHua phoneNumber
无意义后缀 UserDataClass, ListImpl User, UserList
测试函数无空格 shouldReturnUserWhenIdExists() `should return user when id exists`()

# 03.代码格式规范

# 3.1 缩进与空格 【必须】

// ✅ 使用 4 个空格缩进(Kotlin 官方风格)
class UserService(private val repo: UserRepo) {
    fun process(id: Long): User? {
        val cached = cache[id]       // 当可以直接 return 时省略 else
        if (cached != null) return cached

        return repo.findById(id)?.also {
            cache[id] = it
        }
    }
}

// ✅ 运算符两侧加空格
val result = a + b * c
val ok = (x > 0) && (y < 10)

// ✅ 冒号:类型标注时冒号前无空格、后一个空格
fun greet(name: String): String

// ✅ 冒号:继承/实现时冒号两侧各一个空格
class Dog : Animal(), Barkable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 3.2 大括号与尾逗号 【必须】

// ✅ 左大括号不换行,右大括号独占一行
class UserManager {
    fun doWork() {
        if (condition) {
            // ...
        }
    }
}

// ✅ 尾逗号(trailing comma):参数/属性列表最后加逗号
//   好处:diff 干净,重排方便
data class UserDto(
    val id: Long,
    val name: String,
    val email: String? = null,    // ← 尾逗号
)

fun createUser(
    name: String,
    email: String,
    role: Role = Role.USER,       // ← 尾逗号
): User
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 3.3 换行与对齐

// ✅ 参数过多时:每个参数独占一行,缩进 4 空格
fun sendNotification(
    userId: String,
    title: String,
    message: String,
    type: NotificationType = NotificationType.DEFAULT,
): NotificationResult

// ✅ 链式调用:每个操作符前换行,用 . 开头
val names = users
    .filter { it.isActive }
    .map { it.name }
    .sorted()
    .take(10)

// ✅ when 分支过长时换行
return when (status) {
    Status.Active -> buildActiveResponse(user)
    Status.Inactive,
    Status.Pending -> buildDefaultResponse()    // 多条件并列
    else -> throw IllegalStateException("Unknown status: $status")
}

// 单行长度建议不超过 120 字符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 3.4 空行与分隔

class UserService(
    private val repo: UserRepo,
    private val cache: UserCache,
) {
    // 同类型的属性放在一起,不同逻辑组之间空一行
    private val localCache = LruCache<Long, User>(100)

    fun getUser(id: Long): User? {
        val cached = cache[id]
        if (cached != null && !cached.isExpired()) return cached

        return repo.findById(id)?.also {
            cache[id] = it
        }
    }

    fun deleteUser(id: Long) {
        cache.remove(id)
        repo.delete(id)
    }

    // 不同职责的方法组之间空一行
    companion object {
        private const val TAG = "UserService"
        private const val CACHE_TTL_MS = 60_000L
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 04.注释规范

# 4.1 核心原则

注释解释“为什么”,代码说明“做了什么”。

// ❌ 差的注释:复述代码
count++  // count 加 1

// ✅ 好的注释:解释意图
count++  // 跳过 CSV 文件的标题行

// ✅ 记录决策原因
// 使用重试机制,因为第三方接口在高峰期偶尔超时(2023-06 确认的问题)
suspend fun fetchWithRetry(): Data

// ✅ 标注非显而易见的性能考量
// 用 Sequence 替代 List,上游 10w+ 元素,避免中间集合分配
val result = data.asSequence()
    .filter { it.isActive }
    .map { it.transform() }
    .toList()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 4.2 KDoc类注释

/**
 * 用户管理服务,负责用户的 CRUD 和权限管理。
 *
 * 线程安全性:内部使用 [Mutex] 保护共享状态,所有公开方法均线程安全。
 *
 * 使用示例:
 * ```
 * val service = UserService(repo, cache)
 * val user = service.getUser(123)
 * ```
 *
 * @property repo 用户数据源
 * @property cache 本地缓存
 */
class UserService(
    private val repo: UserRepo,
    private val cache: UserCache,
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 4.3 函数注释

/**
 * 根据 ID 查询用户信息。
 *
 * 优先从缓存获取,未命中时查数据库并回填缓存。
 *
 * @param userId 用户唯一标识,必须 > 0
 * @return 用户对象;若不存在则返回 null
 */
fun getUser(userId: Long): User?

/**
 * 批量更新用户状态。
 *
 * @param ids 待更新的用户 ID 集合
 * @param newStatus 目标状态
 * @throws IllegalArgumentException 当 ids 为空时
 */
fun batchUpdateStatus(ids: Set<Long>, newStatus: UserStatus)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 4.4 TODO 与 FIXME

// TODO(yc): 添加分页查询支持,预计 v2.0 实现
fun getAllUsers(): List<User>

// FIXME(yc): userId 为 Long.MAX_VALUE 时缓存 key 冲突,需要改用复合 key
fun cacheUser(userId: Long, user: User)

// HACK(yc): 第三方 SDK 的 bug,等待官方修复后移除(issue: #1234)
fun workaroundForSdkBug()
1
2
3
4
5
6
7
8

# 05.类与对象设计规范

# 5.1 data class 【必须】

// ✅ 纯数据载体 → data class(自动生成 equals/hashCode/toString/copy/componentN)
data class User(
    val id: Long,
    val name: String,
    val email: String? = null,
)

// ✅ 不可变优先:全部用 val
data class Config(val host: String, val port: Int)

// ❌ 可变 data class 只在确实需要时使用,并写明原因
data class MutableCounter(var count: Int)  // 需要时可写

// ❌ 不要用 data class 当"大而全"的对象
//    data class 的 copy() 是浅拷贝,嵌套对象会导致意外共享
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 5.2 sealed class 与 sealed interface 【推荐】

// ✅ sealed class:限制子类的封闭层级
sealed class Result<out T> {
    data class Success<T>(val data: T) : Result<T>()
    data class Error(val code: Int, val message: String) : Result<Nothing>()
    object Loading : Result<Nothing>()
}

// ✅ when 覆盖所有子类时无需 else
fun <T> handle(result: Result<T>) = when (result) {
    is Result.Success -> showData(result.data)
    is Result.Error -> showError(result.code, result.message)
    is Result.Loading -> showLoading()
    // 编译器保证没有遗漏
}

// ✅ Kotlin 1.5+ 可用 sealed interface(更灵活多继承)
sealed interface NetworkState {
    data class Available(val latencyMs: Long) : NetworkState
    data object Unavailable : NetworkState     // data object(Kotlin 1.9+)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 5.3 object 与 companion object 【推荐】

// ✅ 单例用 object(线程安全的懒加载)
object AppConfig {
    val baseUrl: String = "https://api.example.com"
    const val TIMEOUT_MS = 10_000L
}

// ✅ companion object:类级别的常量和工厂方法
class User private constructor(val id: Long, val name: String) {
    companion object {
        const val DEFAULT_NAME = "Anonymous"

        fun create(name: String): User = User(nextId(), name)

        private var currentId = 0L
        private fun nextId() = ++currentId
    }
}

// ❌ 避免在 companion object 中实例化重量级对象(考虑依赖注入)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 5.4 可见性修饰符 【必须】

// ✅ Kotlin 默认 public,但应显式声明内部可见性
class UserRepository(
    private val api: UserApi,          // 构造参数私有
    private val cache: UserCache,
) {
    // 公开 API
    suspend fun getUser(id: Long): User? = ...

    // 内部辅助方法用 private
    private suspend fun refreshCache(id: Long) { ... }
}

// ✅ internal:模块内可见
internal class DatabaseHelper  // 不暴露给模块外部

// ❌ 不要用 public 字段直接暴露可变状态
class BadExample {
    var status: String = ""  // ❌ 直接暴露
}
// ✅ 用 backing property
class GoodExample {
    var status: String = ""
        private set            // ✅ 外部只读
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 5.5 继承与组合 【推荐】

// ✅ 组合优先于继承
class OrderService(
    private val paymentService: PaymentService,    // 组合
    private val notificationService: NotificationService,
)

// ✅ 需要继承时:明确标记 open 和 override
open class Animal {
    open fun speak() = println("...")
}
class Dog : Animal() {
    override fun speak() = println("Woof!")
}

// ✅ 抽象类用于模板方法
abstract class BaseViewModel {
    abstract fun initialState(): State

    fun onError(e: Throwable) {               // 模板方法
        logError(e)
        handleError(e)                        // 子类自定义处理
    }
    abstract fun handleError(e: Throwable)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 06.函数规范

# 6.1 单表达式函数 【推荐】

// ✅ 简单逻辑:单表达式函数体
fun isAdult(age: Int): Boolean = age >= 18
fun greet(name: String) = "Hello, $name!"

// ✅ 不省略返回类型(公开 API 必须保留)
fun calculateTax(income: Long): Double =     // 保留返回类型
    when {
        income < 36_000 -> 0.0
        income < 144_000 -> income * 0.1
        else -> income * 0.2
    }

// ❌ 过长的单表达式应换成块体
// 经验:超过 3 行的单表达式考虑换块体
fun complex(in: String) = in
    .trim()
    .lowercase()
    .replace(" ", "_")
    .take(50)
    .plus(".txt")  // ← 5 行,建议换成块体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 6.2 参数与默认值 【推荐】

// ✅ 默认参数替代重载
fun connect(host: String, port: Int = 8080, useTls: Boolean = true)

// ✅ 命名参数提高可读性(布尔值参数尤其推荐)
connect("api.example.com", useTls = false)   // 调用时语义明确

// ✅ 需要时用 @JvmOverloads 生成 Java 重载版本
class Dialog @JvmOverloads constructor(
    val title: String,
    val message: String = "",
    val positive: String = "确定",
)

// ✅ 参数顺序:必选在前,可选在后
fun search(query: String, page: Int = 1, pageSize: Int = 20)

// ❌ 不要用 Boolean 参数做行为切换 → 拆成两个函数
// fun save(data: Data, sync: Boolean)  ❌
fun saveLocal(data: Data)   // ✅
fun saveRemote(data: Data)  // ✅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 6.3 扩展函数与属性 【推荐】

// ✅ 扩展函数:给既有类添加工具方法
fun String.isEmail(): Boolean =
    matches(Regex("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$"))

fun String.md5(): String = MessageDigest
    .getInstance("MD5")
    .digest(toByteArray())
    .joinToString("") { "%02x".format(it) }

// ✅ 扩展属性:只读计算属性
val View.isVisible: Boolean
    get() = visibility == View.VISIBLE

val Context.screenWidth: Int
    get() = resources.displayMetrics.widthPixels

// ❌ 不要滥用扩展
// ① 仅调用一次就别写成扩展
// ② 别用扩展替代成员函数(业务逻辑应放在类内)
// ③ 扩展函数不能 override,不要期望多态行为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 6.4 中缀与操作符 【推荐】

// ✅ infix:两个对等对象的操作(DSL 风格)
infix fun Int.toward(to: Int): IntProgression = this towardTo to

// ✅ 操作符重载:数学语义明确时使用
data class Vector(val x: Double, val y: Double) {
    operator fun plus(other: Vector) = Vector(x + other.x, y + other.y)
    operator fun times(scalar: Double) = Vector(x * scalar, y * scalar)
}

// ❌ 不要为“看起来很酷”而重载操作符
1
2
3
4
5
6
7
8
9
10

# 6.5 Lambda 与高阶函数 【推荐】

// ✅ 最后一个参数是 lambda 时,花括号写到括号外
users.filter { it.isActive }
     .map { it.name }

// ✅ 唯一参数是 lambda 时,省略括号
thread { doWork() }

// ✅ it 的使用:上下文明确时可用;否则显式命名
users.filter { it.age > 18 }                   // ✅ 上下文清晰
users.map { user ->                            // ✅ 显式命名更清晰
    "${user.firstName} ${user.lastName}"
}

// ✅ 未使用的 lambda 参数用 _ 代替
map.forEach { _, value -> process(value) }

// ✅ 函数引用替代冗余 lambda
users.forEach(::println)                       // ✅
users.forEach { println(it) }                  // ⚠ 等价但冗余

// ✅ 带 receiver 的 lambda:type-safe builder
fun buildHtml(block: HtmlBuilder.() -> Unit): String =
    HtmlBuilder().apply(block).build()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 07.表达式与控制流

# 7.1 when 表达式 【必须】

// ✅ when 替代冗长 if-else 链
fun getStatusColor(status: Status): Int = when (status) {
    Status.Active -> Color.GREEN
    Status.Inactive -> Color.GRAY
    Status.Error -> Color.RED
}

// ✅ when 无参数(多条件布尔)
fun describe(age: Int) = when {
    age < 0   -> throw IllegalArgumentException("invalid age")
    age < 13  -> "儿童"
    age < 18  -> "青少年"
    age < 60  -> "成年人"
    else -> "老年人"
}

// ✅ sealed class + when → 编译器保证穷举
fun handle(netState: NetworkState) = when (netState) {
    is NetworkState.Available -> showOnline(netState.latencyMs)
    is NetworkState.Unavailable -> showOffline()
}
// 编译器不要求 else(因为穷举了所有子类)

// ❌ when 当作命令式用(不推荐)
when (x) {                             // 忽略了返回值
    1 -> println("one")
}
// ✅ 如果只是副作用,考虑用 if 更直接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 7.2 if/try 作为表达式 【推荐】

// ✅ if 作为表达式(无三元运算符)
val user = if (id > 0) getUser(id) else GuestUser

// ✅ try 作为表达式
val result = try {
    parse(input)
} catch (e: JsonSyntaxException) {
    log.warn("Parse failed", e)
    null    // 表达式的值
}

// ✅ 多行表达式:最后一行是值
val status = if (score >= 90) {
    log.info("Excellent")
    "A"
} else if (score >= 60) {
    "B"
} else {
    "C"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 7.3 Elvis 与提前返回 【推荐】

// ✅ Elvis:提供默认值
val displayName = user.name ?: "Unknown"

// ✅ Elvis + 提前返回(常见卫语句模式)
fun process(user: User?) {
    val name = user?.name ?: return       // 空则提前返回
    val email = user.email ?: error("Email required")
    doSomething(name, email)
}

// ✅ Elvis + throw
val config = loadConfig() ?: throw ConfigurationException("Missing config")
1
2
3
4
5
6
7
8
9
10
11
12

# 7.4 解构声明 【可选】

// ✅ data class 自带 componentN,可直接解构
val (name, age) = person
val (id, _, email) = user   // _ 忽略不需要的字段

// ✅ 遍历 Map
for ((key, value) in map) {
    println("$key -> $value")
}

// ✅ Pair/Triple
val (lat, lng) = location.split(",").let { it[0] to it[1] }
1
2
3
4
5
6
7
8
9
10
11

# 7.5 智能类型转换

// ✅ Kotlin 自动智能转换(smart cast)
fun process(obj: Any) {
    if (obj is String) {
        println(obj.length)          // 自动转为 String,可直接访问 .length
    }
}

// ✅ when 中的智能转换
fun describe(obj: Any) = when (obj) {
    is Int -> "整数: $obj"
    is String -> "字符串长度: ${obj.length}"
    else -> "未知类型"
}

// ✅ 安全的类型转换:as?
val user = obj as? User              // 失败返回 null
user?.let { showProfile(it) }

// ❌ 不要用 as 强转(除非 100% 确定类型)
// val user = obj as User            // 类型不匹配抛 ClassCastException
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 08.空安全

# 8.1 安全调用链 【必须】

// ✅ 安全调用 ?. 链式处理可空类型
val city = user?.address?.city?.name

// ✅ 安全调用 + 操作符
val length = user?.name?.length ?: 0
val upper = user?.name?.uppercase()

// ❌ 不要用 if 嵌套替代安全调用链
// if (user != null) { if (user.address != null) { ... } }  // ❌
// ✅ val city = user?.address?.city   // 一行搞定
1
2
3
4
5
6
7
8
9
10

# 8.2 Elvis 操作符进阶 【必须】

// ✅ 基础:提供默认值
val name = user.name ?: "Guest"

// ✅ 提前返回(卫语句)
fun validate(user: User?) {
    val id = user?.id ?: return
    val name = user.name ?: error("name required")
    // ... id 和 name 现在都是非空
}

// ✅ 日志 + 默认值
val config = loadCache(key) ?: run {
    log.warn("Cache miss for $key")
    loadFromNetwork(key)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 8.3 lateinit 与 lazy 【推荐】

// ✅ lateinit:由框架/DI 注入的延迟初始化变量
@Inject lateinit var repository: UserRepository

// ✅ lateinit 检查
if (::repository.isInitialized) {
    repository.getUser(1)
}

// ✅ lazy:首次访问时计算且只计算一次(线程安全默认 SYNCHRONIZED)
private val userService by lazy {
    UserService(createApi(), createCache())
}

// ❌ lateinit 不要用于基本类型(Int, Boolean 等)——会编译错误
// ❌ 能用构造注入就不要用 lateinit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 8.4 平台类型处理 【必须】

// ✅ Java 互操作:Java 返回的引用默认是“平台类型”(T!)
//    立即声明为可空或非空
val name: String = javaUser.getName()        // ✅ 断言非空(风险自负)
val name: String? = javaUser.getName()       // ✅ 标记为可空(安全)
val name = javaUser.getName()                // ❌ 平台类型泄漏,编译器不检查

// ✅ 对 Java 集合也同理
val list: List<User> = javaRepo.findAll()    // ✅ 或 List<User>?
1
2
3
4
5
6
7
8

# 8.5 !! 的使用边界 【必须】

// ❌ 代码审查时,每个 !! 都必须有充分理由
// val name = user!!.name            // ❌ 无理由 → 驳回

// ✅ 唯一可接受的场景:
// 1. 与 Java 框架交互,框架保证非空
// 2. 测试代码中做断言
// 3. 代码逻辑已经保证非空,但编译器推导不出

fun testUser() {
    val user = createTestUser()
    assertNotNull(user)
    assertEquals("Alice", user!!.name)       // 测试中可接受
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 09.作用域函数

# 9.1 函数速查表

函数 对象引用 返回值 是否为扩展 典型场景
let it Lambda 结果 ✅ 非空执行 / 变量限定作用域 / 类型转换
apply this 对象本身 ✅ 对象初始化 / 配置(Builder 风格)
run this Lambda 结果 ✅ 对象配置 + 计算返回值
also it 对象本身 ✅ 附加操作(日志、副作用)
with this Lambda 结果 ❌(非扩展) 对同一对象连续调用多个方法

# 9.2 选型决策树

你需要做什么?

  基于结果做事?──yes──▶ 非空对象?──yes──▶ let
                        │
                        └─ 任意对象 ──▶ also

  配置/初始化对象?──yes──▶ 需要返回值?──yes──▶ run
                            │
                            └─ 返回对象本身 ──▶ apply

  对同一对象多个操作?──yes──▶ with
1
2
3
4
5
6
7
8
9
10
11

# 9.3 典型场景示例

// ✅ let:非空执行
user?.let { showProfile(it) }

// ✅ let:限定作用域 + 类型转换
val name = (obj as? User)?.let { it.firstName + " " + it.lastName }

// ✅ apply:对象配置
val dialog = AlertDialog.Builder(context).apply {
    setTitle("删除确认")
    setMessage("确定要删除这条记录吗?")
    setPositiveButton("确定") { _, _ -> onConfirm() }
    setNegativeButton("取消", null)
}.create()

// ✅ run:对象配置 + 返回计算结果
val validationResult = input.run {
    validate()           // 调用 input 的方法
    toFormattedString()  // 最后一行是返回值
}

// ✅ also:附加操作(不影响主流程)
val user = createUser().also {
    log.info("User created: ${it.id}")
    analytics.track("user_created", mapOf("id" to it.id))
}

// ✅ with:对同一对象的多个调用
with(fontMetrics) {
    val lineHeight = ascent + descent
    val totalHeight = lineHeight + leading
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 9.4 嵌套与链式规则

// ❌ 不要嵌套作用域函数 —— 它是可读性杀手
user?.let { u ->
    repository.save(u).also { saved ->
        cache.put(saved.id, saved).apply {
            log.info("Cached: ${this.id}")    // this 是缓存对象还是 saved?
        }
    }
}

// ✅ 拆分为小函数或分步执行
user?.let { u ->
    val saved = repository.save(u)
    cache.put(saved.id, saved)
    log.info("Cached: ${saved.id}")
}

// ✅ 链式调用不要超过 2 层作用域函数
val result = data
    .filter { it.isValid }
    .map { it.transform() }
    .apply { log.info("Processed ${size} items") }   // ✅ 一层 apply,清晰
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 10.协程规范

# 10.1 结构化并发 【必须】

// ✅ 用 viewModelScope / lifecycleScope(自动随生命周期取消)
class MyViewModel : ViewModel() {
    fun loadData() {
        viewModelScope.launch {   // 当 ViewModel cleared 时自动取消
            val data = repository.fetchData()
            _state.value = UiState.Success(data)
        }
    }
}

// ✅ coroutineScope:并行分解任务(一个失败全部取消)
suspend fun fetchAll(): List<Data> = coroutineScope {
    val deferredA = async { fetchA() }
    val deferredB = async { fetchB() }
    deferredA.await() + deferredB.await()
}

// ✅ supervisorScope:子协程独立容错
suspend fun fetchAllSafe(): List<Data> = supervisorScope {
    val deferredA = async { fetchA() }   // A 失败不影响 B
    val deferredB = async { fetchB() }
    listOf(
        runCatching { deferredA.await() }.getOrNull(),
        runCatching { deferredB.await() }.getOrNull(),
    ).filterNotNull()
}

// ❌ 禁止使用 GlobalScope(生命周期不受控)
// GlobalScope.launch { ... }  // 除非你明确知道要全局运行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 10.2 调度器选择 【必须】

// ✅ 调度器使用指南
// Dispatchers.Main     → UI 更新(Android/Compose)
// Dispatchers.IO       → 网络、文件、数据库
// Dispatchers.Default  → CPU 密集型(排序、解析、加解密)
// Dispatchers.Unconfined → 几乎不用

class UserRepository(private val api: UserApi, private val db: UserDao) {
    suspend fun getUser(id: Long): User {
        return withContext(Dispatchers.IO) {
            // ① 先查缓存
            db.getUser(id)?.let { return@withContext it }
            // ② 网络请求
            api.fetchUser(id).also { db.insertUser(it) }
        }
    }
}

// ❌ 在 suspend 函数中做 IO 操作不切换调度器
//    默认运行在调用方的调度器上——可能意外阻塞 Main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 10.3 异常处理 【必须】

// ✅ launch → 异常向上传播(通过 CoroutineExceptionHandler 捕获)
val handler = CoroutineExceptionHandler { _, e ->
    log.error("Unhandled coroutine exception", e)
}
scope.launch(handler) {
    riskyOperation()  // 异常由 handler 处理
}

// ✅ async → 异常在 await() 处抛出
scope.launch {
    val deferred = async { fetchData() }
    try {
        val data = deferred.await()
    } catch (e: IOException) {
        _error.value = "Network error: ${e.message}"
    }
}

// ✅ runCatching(协程友好)
viewModelScope.launch {
    runCatching { repository.fetchData() }
        .onSuccess { _state.value = it }
        .onFailure { _error.value = it.message }
}

// ❌ 不要让异常静默丢失
// try { ... } catch (e: Exception) {}   // ← 空的 catch,异常被吞
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 10.4 取消与资源释放 【推荐】

// ✅ 协程取消是协作式的:检查 isActive 或调用挂起函数
suspend fun longRunningWork() {
    for (i in 0..1000) {
        ensureActive()          // 检查取消
        doStep(i)
    }
}

// ✅ 用 finally 释放资源
scope.launch {
    val conn = connect()
    try {
        conn.send(data)
    } finally {
        conn.close()            // 无论如何都会释放
    }
}

// ✅ 不可取消的清理代码:withContext(NonCancellable)
try {
    doWork()
} finally {
    withContext(NonCancellable) {
        cleanup()               // 清理必须执行,即使已被取消
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 10.5 Flow 规范 【推荐】

// ✅ Flow 用于冷数据流
fun observeUser(id: Long): Flow<User> = flow {
    while (true) {
        emit(repository.getUser(id))
        delay(5_000)           // 每 5 秒轮询
    }
}.flowOn(Dispatchers.IO)       // 上游在 IO 执行
  .catch { e -> emit(User.EMPTY) }  // 异常处理
  .distinctUntilChanged()       // 去重

// ✅ StateFlow:状态持有(热流)
private val _state = MutableStateFlow(UiState.Loading)
val state: StateFlow<UiState> = _state.asStateFlow()  // 只读暴露

// ✅ SharedFlow:事件总线
private val _events = MutableSharedFlow<Event>()
val events: SharedFlow<Event> = _events.asSharedFlow()

// ❌ 不要用 Channel 做事件总线 → 用 SharedFlow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 11.集合与序列

# 11.1 不可变优先 【必须】

// ✅ 默认使用只读集合接口
fun getUsers(): List<User>           // 只读视图
fun getConfig(): Map<String, String>

// ✅ 需要可变时显式声明
val mutableItems = mutableListOf<String>()

// ✅ 赋值时用 val,内容可变用 MutableList
private val _items = mutableListOf<Item>()    // 内部可变
val items: List<Item> get() = _items          // 外部只读

// ❌ 不要用 var + 不可变集合来"模拟"可变集合
// var list = listOf(1, 2); list += 3  → 每次都是新对象
// ✅ val list = mutableListOf(1, 2); list += 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 11.2 函数式链式调用

// ✅ 链式调用:. 开头,每个操作一行
val activeUserNames = users
    .filter { it.isActive }
    .sortedByDescending { it.lastLogin }
    .map { it.name }
    .take(10)

// ✅ 空安全组合
val firstEmail = users
    .mapNotNull { it.email }       // 过滤 null + 类型转换
    .firstOrNull()

// ✅ 条件构建:用 buildList/buildMap
val list = buildList {
    add("header")
    if (condition) add("optional")
    addAll(loadDynamicItems())
}

// ❌ 频繁修改用 forEach + mutableList(语义不清)
// users.forEach { if (it.isActive) result.add(it.name) }
// ✅ 用 filter + map 更声明式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 11.3 Sequence 序列 【可选】

// ✅ 大数据量链式操作:用 asSequence 惰性求值
val result = largeList.asSequence()     // 10w+ 元素
    .filter { it.isValid }              // 步骤①
    .map { it.transform() }             // 步骤②
    .take(20)                           // 步骤③:只处理前 20 条
    .toList()
// Sequence:逐元素走完整个链条 ①→②→③,找到 20 个后停止
// List:   ① 生成完整中间集合 → ② 再生成完整集合 → ③ 取 20

// ⚠ 小数据量(<1000)直接用 List,Sequence 有开销
// ❌ 对 Sequence 多次迭代 → 每次都会重新执行链条
1
2
3
4
5
6
7
8
9
10
11

# 11.4 分组与聚合

// ✅ groupBy:按条件分组
val usersByRole: Map<Role, List<User>> = users.groupBy { it.role }

// ✅ groupingBy + eachCount(省去中间 map)
val roleCounts = users.groupingBy { it.role }.eachCount()

// ✅ 聚合:sumOf, maxOf, minOf, average
val totalScore = users.sumOf { it.score }
val oldest = users.maxByOrNull { it.age }

// ✅ 分区:partition(满足/不满足)
val (active, inactive) = users.partition { it.isActive }

// ✅ 窗口操作(Kotlin 1.2+)
val sliding = list.windowed(size = 3, step = 1)    // 滑动窗口
val chunked = list.chunked(3)                       // 分块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 12.现代Kotlin特性

# 12.1 类型别名(typealias)【可选】

// ✅ 简化复杂类型签名
typealias UserHandler = (User) -> Unit
typealias UserMap = Map<Long, User>

// ✅ 语义化基础类型
typealias UserId = Long             // 文档即类型

fun getUser(userId: UserId): User   // 比 fun getUser(userId: Long): User 更清晰
1
2
3
4
5
6
7
8

# 12.2 委托属性 【推荐】

// ✅ lazy:延迟初始化(线程安全)
private val repository by lazy { UserRepository(api) }

// ✅ observable:属性变化监听
var name: String by Delegates.observable("<init>") { _, old, new ->
    println("name changed: $old → $new")
}

// ✅ vetoable:属性变化前可拦截
var age: Int by Delegates.vetoable(0) { _, _, new ->
    new in 0..150    // false 则拒绝修改
}

// ✅ map 委托:从 Map 中取值(配置/JSON 反序列化友好)
class UserConfig(map: Map<String, Any>) {
    val name: String by map
    val age: Int by map
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 12.3 内联类与值类 【推荐】

// ✅ Kotlin 1.5+ @JvmInline value class:零开销包装
@JvmInline
value class UserId(val value: Long)

@JvmInline
value class Password(val value: String) {
    init { require(value.length >= 8) }
}

// ✅ 类型安全:不同语义的类型不能直接混用
fun authenticate(userId: UserId, password: Password)
// authenticate(Password("12345678"), UserId(1L))  // ❌ 编译错误!

// ✅ 可以有接口和成员
@JvmInline
value class Email(val address: String) {
    val domain: String get() = address.substringAfter("@")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 12.4 内联函数与 reified 【推荐】

// ✅ inline + reified:运行时访问泛型类型信息
inline fun <reified T> Gson.fromJson(json: String): T =
    fromJson(json, T::class.java)

// 调用
val user: User = gson.fromJson(jsonString)  // 无需传 Class

// ✅ inline 用于高阶函数(减少函数对象分配)
inline fun measureTime(block: () -> Unit): Long {
    val start = System.nanoTime()
    block()
    return System.nanoTime() - start
}

// ❌ 非高阶函数不要 inline(无收益,反而增大字节码)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 12.5 契约 Contracts 【可选】

// ✅ contracts 帮助编译器推导类型
@OptIn(ExperimentalContracts::class)
fun require(condition: Boolean) {
    contract { returns() implies condition }     // condition 为 true 才能返回
    if (!condition) throw IllegalArgumentException()
}

fun test(s: String?) {
    require(s != null)
    println(s.length)     // 编译器知道 s 非空!
}
1
2
3
4
5
6
7
8
9
10
11

# 13.工具链与自动化

# 13.1 ktlint格式化

// ktlint:Kotlin 官方 linter + formatter,零配置
// 安装:brew install ktlint
// 使用:
//   ktlint --format          → 格式化
//   ktlint                   → 仅检查

// .editorconfig(ktlint 读取的配置)
// [*.kt]
// indent_size = 4
// max_line_length = 120
// insert_final_newline = true
1
2
3
4
5
6
7
8
9
10
11

# 13.2 detekt静态分析

# detekt:Kotlin 静态代码分析(复杂度、坏味道、潜在 bug)
# detekt-config.yml
style:
  MagicNumber:
    active: true
    ignoreNumbers: ['-1', '0', '1', '2']
  WildcardImport:
    active: true
complexity:
  TooManyFunctions:
    active: true
    thresholdInFiles: 15
coroutines:
  GlobalCoroutineUsage:
    active: true           # 检测 GlobalScope 使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 13.3 CI 集成

# GitHub Actions 示例
- name: Kotlin Code Quality
  run: |
    ./gradlew ktlintCheck          # 格式检查
    ./gradlew detekt               # 静态分析
    ./gradlew lint                 # Android Lint
    ./gradlew test                 # 单元测试
1
2
3
4
5
6
7

# 13.4 pre-commit 钩子 【推荐】

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/pinterest/ktlint
    rev: 1.2.1
    hooks:
      - id: ktlint
1
2
3
4
5
6
# 安装
pip install pre-commit
pre-commit install           # 每次 commit 前自动检查
pre-commit run --all-files   # 手动全量检查
1
2
3
4

# 14.常见反模式

反模式 问题 改进
!! 断言 隐藏 NPE,crash 时无上下文 安全调用 ?. / Elvis ?: / requireNotNull()
GlobalScope 生命周期不受控,进程级泄漏 viewModelScope / lifecycleScope / coroutineScope
data class 默认值过多(>5) 构造复杂,copy 语义模糊 Builder 模式或拆分
过度使用 var 可变状态难跟踪 优先 val,确需可变用 MutableStateFlow
嵌套作用域函数 可读性灾难 最多一层,超过则拆分函数
it 含义不明 list.filter { }.map { it.name } 哪个 it? 嵌套/链式 >1 层时显式命名
忽略协程异常 launch { ... } 无 handler,异常被静默丢弃 加 CoroutineExceptionHandler 或 supervisorScope
用 MutableList 对外暴露 fun getItems(): MutableList<T> 外部可修改 返回 List<T> 或 toList()
Java 互操作忽略可空 val s: String = javaMethod() 可能 NPE 显式标记 String? 或做 null 检查

# 15.代码审查清单

每次 Code Review 时,按以下清单逐项检查:

## 命名
- [ ] 类大驼峰,函数/属性小驼峰,常量全大写
- [ ] 无拼音、单字母、含义模糊的命名
- [ ] 布尔返回值函数/属性用 is/has 前缀
- [ ] 测试函数用反引号 + 空格

## 空安全
- [ ] 无 !! 断言(除非有充分理由且注释说明)
- [ ] 可空类型用 ?. 或 ?: 安全处理
- [ ] lateinit/lazy 使用合理
- [ ] Java 互操作返回值显式声明可空性

## 协程
- [ ] 用结构化并发(viewModelScope / lifecycleScope),不用 GlobalScope
- [ ] IO 操作在 Dispatchers.IO
- [ ] 异常有正确捕获(CoroutineExceptionHandler 或 runCatching)
- [ ] 无协程泄漏(launch 在合适作用域)

## 类设计
- [ ] 优先 val 而非 var
- [ ] 数据类用 data class
- [ ] 密封类限定子类(sealed class / sealed interface)
- [ ] 组合优先继承
- [ ] 类成员可见性合理(非 public 尽量 private/internal)

## 函数
- [ ] 单函数不超过 40 行
- [ ] 卫语句减少嵌套
- [ ] 扩展函数语义清晰,无滥用
- [ ] Lambda 中 it 含义明确(>1 层嵌套时显式命名)

## 作用域函数
- [ ] 无嵌套作用域函数(>1 层)
- [ ] 选型正确(apply 配置 / let 非空 / also 副作用)
- [ ] 链式调用不超过 2 层

## 集合
- [ ] 优先只读接口(List / Set / Map)
- [ ] 大数据量链式操作考虑 Sequence
- [ ] 对外暴露不带可变性

## 测试
- [ ] 核心逻辑有单元测试
- [ ] 正常和异常路径都覆盖
- [ ] 协程测试用 runTest / StandardTestDispatcher
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

# 16.常见陷阱速查

以下陷阱即使在有经验的开发者中也常见,值得定期回顾。

# 16.1 空安全陷阱

# 陷阱 描述 正解
1 平台类型 NPE val s: String = javaMethod() Java 返回 null 直接 NPE 显式标记 String? 或马上判断
2 !!.let 无意义 user!!.let { } 还是抛 NPE user?.let { }
3 lateinit 未初始化 忘记检查 isInitialized 尽量用构造注入或 lazy
4 requireNotNull 丢信息 抛异常但无业务上下文 带上消息:requireNotNull(x) { "id required" }

# 16.2 协程陷阱

# 陷阱 描述 正解
1 launch 异常丢失 无 handler,异常被静默丢弃 CoroutineExceptionHandler / supervisorScope
2 async 未 await 异常在 deferred 中,不调用 await 永远不会抛出 runCatching { deferred.await() }
3 子协程泄漏 父协程取消但子协程未取消 用 coroutineScope 保证结构化并发
4 withContext 意外嵌套 withContext(IO) { withContext(Main) { ... } } 多余切换 只在外层切换,内部不重复 wrap
5 runBlocking 在主线程 Android 上阻塞主线程 → ANR 测试用 runTest,业务代码禁止 runBlocking

# 16.3 集合陷阱

# 陷阱 描述 正解
1 listOf 是只读视图 如果原 list 可变,listOf 看到的是快照吗?不,是实时视图 需要快照用 toList()
2 map{} 后用 filter{} → 多余分配 先生成完整中间集合再过滤 颠倒顺序:先 filter{} 再 map{}
3 forEach + return return 是返回到外层函数,不是跳出循环 用 forEach + return@forEach 或直接 for
4 + 操作符产生新集合 val list = listOf(1); list += 2 是 O(n) 新对象 频繁修改用 mutableListOf

# 16.4 类型与泛型陷阱

# 陷阱 描述 正解
1 data class 的 copy() 浅拷贝 嵌套可变对象被共享 重写 copy 或避免 data class 包装可变对象
2 Nothing 的协变/逆变 List<Nothing> 是 List<String> 的子类型吗?是 理解声明处型变
3 泛型实化只在线联 非 inline 函数无法获知泛型具体类型 用 reified + inline
4 sealed class 子类位置 子类必须在同一个文件或同一个 package(Kotlin 1.9 已放宽) 了解版本差异

本文档将随 Kotlin 语言演进持续更新,欢迎通过 GitHub issues (opens new window) 反馈问题和建议。

#Kotlin#代码规范
上次更新: 2026/06/17, 11:39:29
Go编程代码规范指南
Swift编程代码规范指南

← Go编程代码规范指南 Swift编程代码规范指南→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式