不止是“打印代码”:从 APT/JavaPoet 到 KSP/KotlinPoet 的现代代码生成思维

331 阅读3分钟

一句话总结:

在动手“打印代码”前,先问自己三个问题:这个问题是否已被官方工具解决?我的项目是 Kotlin 优先吗?如果是,那么 KSP + KotlinPoet 就是你性能更高、体验更佳的现代化“代码打印机”。


第一章:经典永流传——APT 与 JavaPoet 的“屠龙之术”

APT 与 JavaPoet 的组合,是 Java 生态中代码生成的基石,其实践技巧历久弥新。

核心战术回顾:

  • 架构上: 将注解、处理器、API 拆分为独立模块,实现高度解耦。

  • 处理器中 (APT):

    • 使用 processingEnv.getMessager() 进行日志打印,这是唯一的调试窗口。
    • 通过 Filer 写入文件,并处理好 IOException 以避免重复生成。
    • 理解 RoundEnvironment,为多轮处理做好准备。
  • 代码生成中 (JavaPoet):

    • 熟练运用链式调用和占位符($T, $S, $L 等)来生成清晰、自动导包的代码。
    • 使用 ParameterizedTypeName 等工具类优雅地处理泛型。
    • 利用 google/auto-service 库的 @AutoService 注解,实现处理器的自动注册。

掌握这些,你便拥有了驾驭这台经典“代码打印机”的全部能力。但现在,我们需要抬起头,看看周围的世界发生了什么变化。


第二章:停下来思考——我们真的需要“造轮子”吗?

代码生成的初衷是为了消除“样板代码”(Boilerplate)。但在动手之前,请先检查你的“工具箱”,因为官方可能已经提供了更强大的“电动工具”。

你想解决的问题别急着用 APT/KSP,优先考虑...
厌倦了 findViewByIdView Binding (官方、零注解、类型安全)
厌倦了写 Parcelable@Parcelize 注解 (Kotlin 官方插件)
厌倦了 JSON 手动解析Moshi 的 codegen 模块 (基于 kapt/KSP 自动生成 Adapter)
厌倦了手动写 RecyclerView.AdapterEpoxy / Groupie 等成熟的列表框架

核心思想: 在重复造轮子之前,先去拥抱社区和官方已经沉淀的最佳实践。最好的代码,是不用写的代码。


第三章:新时代的“高速打印机”——KSP 与 KotlinPoet

如果经过深思熟虑,你确定需要自己动手生成代码,并且你的项目是(或将是)Kotlin-First 的,那么请毫不犹豫地选择 KSP + KotlinPoet

为什么 KSP 是更优的选择?

  • 性能: KSP 直接解析 Kotlin 源码,无需 kapt 的“Java 存根生成”这一中间步骤,编译速度通常能提升 1.5 到 2 倍
  • 语言理解力: KSP 能够完整地理解 Kotlin 的所有语言特性(可空性、suspend 等),而 APT 只能看到这些特性被“翻译”成 Java 后的样子。
  • 现代 API: KSP 的 API (SymbolProcessor, Resolver) 相比 APT 的 AbstractProcessor 更加现代化和 Kotlin-friendly。

KotlinPoet:为 Kotlin 而生的代码生成器

KotlinPoet 是 Square 公司为 Kotlin 打造的、与 JavaPoet 师出同门的库。它的 API 风格极其相似,但为生成 Kotlin 代码提供了原生支持。

// 使用 KotlinPoet 生成一个 Kotlin 文件
val fileSpec = FileSpec.builder("com.example.myapp", "HelloWorld")
    .addFunction(
        FunSpec.builder("main")
            .addStatement("println(%S)", "Hello, KotlinPoet!")
            .build()
    )
    .build()

fileSpec.writeTo(System.out) // 输出生成的代码

结论: KSP 负责“输入”,KotlinPoet 负责“输出” 。这个组合是现代 Android 代码生成的黄金搭档。


四、总结:你的现代代码生成决策流程

graph TD
    A[开始: 我需要消除样板代码] --> B{是否有官方/成熟的<br>高级工具能解决这个问题?};
    B -- 有 --> C[✅ 优先使用官方/高级工具<br>(如 View Binding, @Parcelize)];
    B -- 没有, 必须自己生成代码 --> D{我的项目是 Kotlin-First 吗?};
    D -- 是 --> E[✅ 最佳实践:<br>使用 KSP + KotlinPoet];
    D -- 否, 是纯 Java 项目<br>或需要兼容旧的 APT 库 --> F[⚠️ 备用方案:<br>使用 APT/kapt + JavaPoet];
    
    C --> Z[结束];
    E --> Z[结束];
    F --> Z[结束];

通过这套决策流程,你将不再仅仅是一个懂得如何使用代码生成工具的“工匠”,而是一个能够根据项目背景、技术演进和投入产出比,做出最明智技术选型的“架构师”。