第71章:代码风格——Kotlin编码规范、ktlint、detekt、Spotless

代码风格这事儿,说大不大,说小不小。

我见过不少团队,功能写得飞起,但一打开代码库,缩进乱七八糟、命名随心所欲、空行全看心情。说实话,这种代码看着就累。更别提接手维护的时候,那种想砸键盘的冲动——嗯,我经历过。

Kotlin 这门语言本身就很讲究「优雅」。如果你写出来的代码跟 Java 似的,那还不如用 Java。今天我们就聊聊怎么让 Kotlin 代码既规范又漂亮,顺便用工具把这些事自动化掉。

为什么要在意代码风格?

你可能觉得:「能跑不就行了?」

我以前也这么想。直到有一次,我接手一个遗留项目,里面同一个文件里混着四种不同的命名风格——驼峰、下划线、匈牙利命名法,甚至还有拼音缩写。我花了整整两天才搞清楚一个核心类的逻辑。从那以后,我对代码风格的态度就变了。

说白了,代码风格不是给机器看的,是给人看的。机器只关心语法对不对,人还得关心逻辑清不清晰、结构合不合理。统一的风格能让你少掉很多头发。

核心观点: 代码风格 = 团队沟通成本。风格越统一,沟通成本越低。

Kotlin 官方编码规范

Kotlin 官方其实有一套非常详细的编码规范。我挑几个重点说说,都是我在项目中踩过坑的地方。

命名规则

  • 类名:大驼峰(PascalCase)。比如 UserRepositoryHttpClient
  • 函数名/变量名:小驼峰(camelCase)。比如 getUserById()userName
  • 常量:大写加下划线。比如 MAX_RETRY_COUNTDEFAULT_TIMEOUT
  • 包名:全小写,不要用下划线。比如 com.example.myapp

这里有个坑:Kotlin 的 const val 只能用在顶层或者 object 里。我见过有人把常量写在 companion object 里,结果发现编译报错——嗯,注意一下。

冒号与空格

Kotlin 的冒号前后空格有讲究:

// 正确
class User : BaseEntity
val map: Map<String, Int> = emptyMap()

// 错误
class User:BaseEntity
val map:Map<String,Int>=emptyMap()

我个人习惯在冒号前加一个空格,后面也加一个。这样看着舒服,也符合官方规范。

Lambda 表达式

Kotlin 的 lambda 很常用,但写法上容易乱。官方建议:

// 推荐
list.filter { it.isActive }
      .map { it.name }

// 不推荐
list.filter({ it.isActive }).map({ it.name })

如果 lambda 是函数的最后一个参数,直接写在括号外面。这是 Kotlin 的特色,别浪费了。

ktlint:格式化你的代码

ktlint 是我用得最多的工具。它不光是检查,还能自动修复。说白了,你写完代码跑一下 ktlint --format,它就把缩进、空格、换行这些琐事全帮你搞定了。

小技巧: 我习惯在 Git 的 pre-commit hook 里加 ktlint 检查。这样每次提交前自动格式化,省心。

安装方式很简单:

// Gradle 配置
plugins {
    id("org.jlleitschuh.gradle.ktlint") version "11.0.0"
}

// 运行
./gradlew ktlintFormat

ktlint 的规则是硬编码的,不能自定义。这其实是个好事——省得团队里为「到底用几个空格缩进」吵半天。

detekt:静态分析利器

ktlint 管的是格式,detekt 管的是「代码质量」。它会检查你的代码有没有潜在问题,比如:

  • 函数太长(默认超过 20 行就报警)
  • 嵌套太深(超过 4 层就提示)
  • 未使用的变量或参数
  • 复杂的表达式

我记得有一次,detekt 帮我发现了一个隐藏的 bug——一个变量被赋值了两次,但第二次赋值其实是拼写错误。这种问题肉眼很难看出来,但工具一抓一个准。

// detekt 配置示例(detekt.yml)
complexity:
  LongParameterList:
    threshold: 6
  LargeClass:
    threshold: 300

style:
  MaxLineLength:
    maxLineLength: 120

detekt 的规则可以自定义。你可以根据团队情况调整阈值。比如我习惯把函数长度阈值设到 30 行,因为有些业务逻辑确实没法拆得太碎。

注意: detekt 的规则别开太严。我见过有人把所有规则都打开,结果项目里全是警告,最后大家干脆无视了。适可而止,抓大放小。

Spotless:统一全项目格式

Spotless 是个通用格式化工具,不光支持 Kotlin,还支持 Java、Groovy、Markdown 等。如果你项目里混着多种语言,用 Spotless 可以统一管理。

// Gradle 配置
spotless {
    kotlin {
        ktlint()
        target("**/*.kt")
    }
    kotlinGradle {
        ktlint()
        target("*.gradle.kts")
    }
}

// 运行
./gradlew spotlessApply

Spotless 的好处是「一键格式化」。你跑一次,整个项目的代码风格就统一了。我一般在 CI 里加一步 spotlessCheck,如果格式不对,构建直接失败。

工具对比与选择

这三个工具各有侧重,我整理了个表格:

工具 主要功能 能否自定义 推荐场景
ktlint 代码格式化 否(硬编码规则) 快速统一格式
detekt 静态分析 是(可配置阈值) 发现潜在问题
Spotless 多语言格式化 是(可配置引擎) 全项目统一管理

我个人建议:ktlint + detekt 组合使用。ktlint 管格式,detekt 管质量。如果项目里还有其他语言,再加个 Spotless。

避坑指南

我曾经在一个项目里同时用了 ktlint 和 detekt,结果发现它们对某些规则有冲突。比如 ktlint 要求 import 语句按字母排序,但 detekt 默认不检查这个。后来我统一用 ktlint 的规则,detekt 只开质量相关的检查。

还有一次,我把 detekt 的 MaxLineLength 设成了 80,结果发现 Kotlin 的链式调用经常超长。后来改到 120,世界清净了。

总结一下: 工具是死的,人是活的。别为了追求「零警告」而牺牲代码可读性。该换行就换行,该注释就注释。

知识体系图

下面这张图展示了本章的核心逻辑:

Kotlin 代码风格 ktlint detekt Spotless 格式化代码 硬编码规则,自动修复 静态分析 可配置阈值,发现隐患 多语言格式化 统一管理,CI 集成 推荐组合:ktlint + detekt

写在最后

代码风格这件事,说白了就是「约定」。没有绝对的对错,只有是否统一。我见过用 2 空格缩进的团队,也见过用 4 空格的,只要全项目一致,都没问题。

工具只是辅助,关键还是人。你想想看,如果每个人都自觉遵守规范,那工具其实可有可无。但现实是——人总会偷懒,总会疏忽。所以,让工具来兜底吧。

我的建议: 新项目一开始就把 ktlint 和 detekt 配好。别等到代码写了几万行再回头格式化,那时候改起来想死的心都有。

嗯,今天就聊到这儿。希望你的 Kotlin 代码,既优雅又健壮。


公众号:蓝海资料掘金营,微信 deep3321