KotlinPoet 详解:Square 出品的 .kt 源码生成库
写注解处理器、KSP 处理器或 Gradle 代码生成插件时,最后一步总要"把代码写成文本文件"——字符串拼接缩进、转义、import 排序,全是脏活。Square 开源的 square/kotlinpoet(KotlinPoet)把这步变成了 API 调用:用 Kotlin/Java 代码描述类、函数、属性与 KDoc,输出格式完美、可直接编译的 .kt 源文件。它源自 JavaPoet 血统,是 Moshi、Room 等知名框架代码生成的幕后工具,文档站 square.github.io/kotlinpoet。本文从核心模型、API 用法、典型集成到实践技巧,完整拆解。
1、项目概述
KotlinPoet 定位为 Kotlin 与 Java API 的 .kt 源文件生成库,由 Square(Okhttp、Retrofit、Moshi 的出品方)维护,2017 年开源,血承 JavaPoet——topics 里直接标注 javapoet。它不关心你为什么生成代码(编译期注解处理、构建脚本、脚手架工具都行),只负责把"代码的树状描述"渲染成合法、带正确 import 与缩进的 Kotlin 源文件。
基本形态与生态:
- 规模:约 4.2k Star、316 Fork,主语言 Kotlin,GitHub Discussions 开放
- 协议为 Apache-2.0(版权 2017 Square, Inc.)
- 近期仍在活跃推送(2026-08 仍有 commit),默认分支 main
- 同族:JavaPoet(.java 生成)、SwiftPoet(Swift 生成),Square "Poet" 系列三兄弟
- 用户:Moshi(codegen 模式)等大量注解处理器以其为输出层
2、四大核心能力
KotlinPoet 的能力可以归成四根支柱。
四根支柱:
- 类型模型:TypeName 体系精确描述类、泛型、可空、函数类型
- 成员构建:TypeSpec / FunSpec / PropertySpec 组合出类、函数、属性
- 代码块:CodeBlock 以占位符 %L %S %T %M 表达字面量与引用
- 文件输出:FileSpec 统一管理包名、import 去重排序与写出
能力要点:
1. import 自动收集:类型引用自动收敛到 import 语句,全限定名只在冲突时出现
2. KModifier 枚举覆盖 Kotlin 修饰符(suspend、inline、operator……)
3. KDoc 与注解皆为一等公民,链式添加
3、核心模型:Spec 家族
KotlinPoet 的 API 围绕一组 Spec 构建器展开,层层嵌套即代码结构。
val file = FileSpec.builder("com.example.hello", "HelloWorld")
.addType(
TypeSpec.classBuilder("HelloWorld")
.addKdoc("由 KotlinPoet 生成的示例类\n")
.primaryConstructor(
FunSpec.constructorBuilder()
.addParameter("name", String::class)
.build()
)
.addProperty(
PropertySpec.builder("name", String::class)
.initializer("name")
.build()
)
.addFunction(
FunSpec.builder("greet")
.addModifiers(KModifier.PUBLIC, KModifier.INLINE)
.returns(String::class)
.addStatement("return \"Hello, \$name\"")
.build()
)
.build()
)
.build()
file.writeTo(System.out) // 输出到控制台,或 writeTo(file 目录)模型要点:
1. FileSpec 是顶层容器:管包名、import、顶层声明
2. TypeSpec 承载 class / object / interface / enum,可嵌套
3. FunSpec 覆盖普通函数、构造器、getter/setter、companion 内函数
4. writeTo 可指向目录、输出流,KSP 场景配 codeGenerator
4、CodeBlock 与占位符
语句级代码用 CodeBlock 描述,占位符决定参数如何渲染,是 KotlinPoet 最核心的设计。
// %L:字面量,原样输出
CodeBlock.of("val x = %L", 42) // val x = 42
// %S:字符串,自动加引号与转义
CodeBlock.of("val s = %S", "hi\n") // val s = "hi\n"
// %T:类型,自动收敛 import
CodeBlock.of("val list = mutableListOf<%T>()", List::class.asClassName())
// %M:成员引用(扩展函数、伴生常量)
CodeBlock.of("%M(1..10)", "kotlin.collections".asName().let {
MemberName("kotlin.collections", "sum") })
// %N:另一个 Spec 的名字
val nameParam = ParameterSpec.builder("name", String::class).build()
CodeBlock.of("println(%N)", nameParam) // println(name)
// beginControlFlow / endControlFlow 处理控制流
FunSpec.builder("printAll")
.addStatement("list.forEach {")
.addStatement(" println(it)") // 也可用控制流 API
.build()占位符要点:
1. %T 是精髓:传类型即自动 import,杜绝手写全限定名
2. %M 专治扩展函数与顶层函数引用
3. 字符串插值需转义 \$,避免与 % 占位符混淆
4. 多参数用相对/位置索引(%1L)可复用同一参数
5、与 KSP / KAPT 集成
KotlinPoet 是注解处理器的标准输出层,与 KSP(Kotlin Symbol Processing)配合是当下主流姿势。
// build.gradle.kts(处理器侧)
dependencies {
implementation("com.squareup:kotlinpoet:2.2.0")
implementation("com.google.devtools.ksp:symbol-processing-api:2.1.0-1.0.29")
}
// 处理器内生成文件
class MyProcessor(private val codeGenerator: CodeGenerator) : SymbolProcessor {
override fun process(resolver: Resolver): List<KSIssue> {
resolver.getSymbolsWithAnnotation("com.example.Gen")
.filterIsInstance<KSClassDeclaration>()
.forEach { decl ->
val fileSpec = buildFactoryFor(decl) // 组装 FileSpec
val deps = Dependencies(false, decl.containingFile)
codeGenerator.createNewFile(deps, pkg, name)
.use { out -> fileSpec.writeTo(out) } // 写入 KSP 输出
}
return emptyList()
}
}集成要点:
1. 版本以 Maven Central 最新为准,示例坐标仅示意
2. KSP 比 KAPT 快且不生成 Java 存根,新项目首选
3. Dependencies 标记源文件,支持增量处理
4. Gradle 插件(buildSrc/插件项目)里也能直接用,不限于编译期
6、典型应用场景
凡是"重复模式代码想自动化"的地方,都是 KotlinPoet 的主场。
常见场景:
- 注解处理器 / KSP:生成工厂、Builder、序列化器(Moshi 同款路数)
- 编译期校验:检测注解后生成辅助类或报告
- 脚手架工具:按模板批量生成 MVVM 页面、Repository 样板
- DSL / 协议转译:把 GraphQL schema、Proto 翻译成 Kotlin 模型
- API 文档辅助:读取源码符号生成调用示例
使用提醒:
1. 生成代码同样要可读:KDoc、命名别偷懒,产出会被 review
2. 大量 addStatement 拼长函数时,考虑抽 lambda 参数化
3. 生成结果务必纳入编译与单测,防格式回归
7、定位对比:代码生成方案三选一
把 KotlinPoet、JavaPoet 与模板引擎(FreeMarker 等)放在一张桌上,各自的位置清晰起来。
三者对比:
- KotlinPoet:Kotlin 专属模型,可空/泛型/扩展函数原生表达,import 智能管理
- JavaPoet:生成 .java 的同族前辈,Java 语法完备但无 Kotlin 概念
- 模板引擎:文本替换思路,灵活但不懂语法,转义与格式全靠自己
选型建议:
1. 生成 Kotlin 代码 → KotlinPoet 无悬念
2. 生成 Java 代码 → JavaPoet
3. 生成非代码文本(配置、文档)→ 模板引擎更顺手
4. 组合玩法:KSP 解析 + KotlinPoet 生成,Room/Moshi 同款管线
8、总结
KotlinPoet 是 Square 出品的 .kt 源码生成库(约 4.2k Star、Apache-2.0):以 FileSpec/TypeSpec/FunSpec/PropertySpec 构建器描述代码结构,CodeBlock 占位符(%L/%S/%T/%M/%N)精确渲染字面量、字符串、类型与成员,import 自动收集去重;2017 年开源至今持续演进,是 KSP/KAPT 注解处理器与脚手架工具的标准输出层,Moshi 等名框架的幕后功臣。
实践技巧:写注解处理器时可先手写一份理想产物,再反推 Spec 组装,事半功倍;占位符优先用 %T 传类型而非拼字符串,import 卫生全靠它;生成的代码需纳入编译与单测,用快照或黄金文件防格式回归。生成代码同样会被 review,KDoc 与命名不可省略。

