Introduction
Code generation is one of Java’s superpowers. Annotation processors run at compile time, generating boilerplate code that developers would otherwise write by hand — builders, serializers, factory methods, and more. The JVM ecosystem offers several libraries that make writing annotation processors both productive and maintainable.
Three libraries dominate this space: JavaPoet (10,851 ⭐) from Square, KotlinPoet (4,147 ⭐) also from Square, and AutoValue (10,560 ⭐) from Google. While all three generate source code, they serve different roles — JavaPoet and KotlinPoet are general-purpose code generation APIs, while AutoValue is a specific annotation processor that uses code generation to eliminate value-type boilerplate.
This article compares these libraries, showing when to use a programmatic code writer, when to use Kotlin’s syntax-aware generator, and when an opinionated annotation processor solves your problem out of the box.
Library Overview
| Feature | JavaPoet | KotlinPoet | AutoValue |
|---|---|---|---|
| Stars | 10,851 | 4,147 | 10,560 |
| Maintainer | Square | Square | |
| Target Language | Java | Kotlin | Java |
| Approach | Programmatic API | Kotlin DSL | Annotation Processor |
| Learning Curve | Moderate | Moderate (Kotlin knowledge) | Low (just add annotations) |
| Customization | Full control over output | Full control over Kotlin output | Extension-based |
| IDE Integration | Generated sources visible | Generated sources visible | Seamless (incremental) |
| Build System | javac / kapt / ksp | kapt / ksp | javac / kapt |
JavaPoet: Programmatic Java Code Generation
JavaPoet provides a fluent API for generating .java source files. Instead of string concatenation (which inevitably produces broken code), JavaPoet models Java language constructs as objects:
| |
JavaPoet excels at generating complex code patterns. A real-world annotation processor using JavaPoet can generate entire classes from annotated interfaces:
| |
Key strengths: Type-safe code generation prevents syntax errors; handles imports automatically; generates well-formatted output; battle-tested in Dagger, Retrofit, and thousands of other libraries.
KotlinPoet: Kotlin-Aware Code Generation
KotlinPoet is the Kotlin counterpart, generating .kt files with full awareness of Kotlin language features — nullability, data classes, extension functions, and coroutines:
| |
KotlinPoet handles Kotlin-specific constructs that JavaPoet cannot express:
| |
For projects using Kotlin Symbol Processing (KSP), KotlinPoet integrates directly:
| |
AutoValue: Opinionated Value Type Generation
AutoValue takes a different approach. Instead of a programmatic API, you define an abstract class or interface, and AutoValue generates the implementation:
| |
AutoValue generates equals(), hashCode(), toString(), and a builder automatically. With extensions, it handles even more:
| |
Key strengths: Zero boilerplate for value types; IDE support with generated sources in build output; incremental annotation processing for fast builds; ecosystem extensions for Gson, Moshi, Room, and more.
When to Use Each Library
| Use Case | Recommended Library |
|---|---|
| Generating builders, factories, or mappers | JavaPoet / KotlinPoet |
| Replacing hand-written value classes | AutoValue |
| Multi-module code generation | JavaPoet with kapt or ksp |
| Kotlin-first project with KSP | KotlinPoet |
| Simple data classes (Java) | AutoValue |
| Complex code patterns (Dagger, Room-like) | JavaPoet |
| Extension function generation | KotlinPoet |
Why Self-Host Your Code Generation Pipeline?
Annotation processing and code generation reduce entire categories of bugs — no more mismatched equals() implementations or forgotten builder fields. When you invest in compile-time code generation:
Catch errors at compile time, not runtime — generated code is verified by the compiler before your tests even run. For more JVM quality tools, see our Java testing libraries guide.
Eliminate boilerplate systematically — one annotation processor can replace thousands of lines of repetitive code across your codebase. Our JUnit 5 vs TestNG comparison shows how generated test scaffolding saves time.
Enforce architectural patterns — code generation ensures every class follows the same conventions. See our object mapping libraries comparison for another pattern where generated code outperforms reflection.
Deployment Architecture for Code Generation
Annotation processors integrate directly into your build pipeline. For Gradle:
| |
For Maven:
| |
Generated sources appear in build/generated/sources/annotationProcessor/ and are automatically included in compilation. No runtime dependencies — the generated code is plain Java or Kotlin.
Migration and Coexistence Strategies
Adopting code generation in an existing codebase requires a gradual approach. Here are patterns for introducing JavaPoet, KotlinPoet, and AutoValue without disrupting development:
Incremental AutoValue Adoption: Start by converting hand-written value classes one at a time. AutoValue generates a separate implementation class, so existing code continues to compile during migration. Use IDE refactoring to extract the abstract class from existing concrete implementations:
| |
Custom Annotation Processor Testing: When writing custom processors with JavaPoet, test them by compiling test sources and verifying the generated output. Google’s compile-testing library makes this straightforward:
| |
KotlinPoet with Multiplatform: For Kotlin Multiplatform projects, KotlinPoet can generate common code that works across JVM, JS, and Native targets. Use expect/actual declarations with generated implementations to share code generation logic while accommodating platform differences.
Build Integration Considerations: When a project uses both Java and Kotlin annotation processors, processor ordering matters. Configure your build to run JavaPoet-based processors first (they generate Java sources), then KotlinPoet processors, and finally AutoValue. For Gradle with KSP, use the ksp configuration block with explicit processor class names to control execution order.
FAQ
Does AutoValue work with Kotlin?
AutoValue targets Java, but Kotlin’s data class provides equivalent functionality natively. For mixed Java/Kotlin projects, use AutoValue for Java classes and data class for Kotlin. If you need shared value types, consider defining them in Kotlin or using KotlinPoet to generate cross-language compatible code.
How does JavaPoet compare to string templates?
JavaPoet prevents common string-concatenation bugs — missing imports, broken indentation, duplicate method names. It also handles reserved keywords and generates valid Java syntax guaranteed. For simple cases, string templates work, but for annotation processors that generate hundreds of classes, JavaPoet’s type safety pays for itself.
Can I use both JavaPoet and AutoValue in the same project?
Yes — they serve different purposes. AutoValue generates implementations for annotated value types, while JavaPoet is used inside custom annotation processors. Many projects use AutoValue for their data models and JavaPoet for generating API clients, serializers, or test fixtures.
What’s the performance impact of annotation processing?
Annotation processing runs at compile time with zero runtime overhead. However, complex processors can slow down builds. Use incremental annotation processing (supported by both JavaPoet and AutoValue) and consider KSP over kapt for Kotlin projects — KSP is up to 2x faster.
Is JavaPoet still relevant with Java records?
Java records (Java 16+) handle simple data carriers, but JavaPoet remains essential for generating complex code — builders, protocol buffers, API clients, and database mappers. Records replace some AutoValue use cases, but annotation processors generating non-trivial code patterns still need JavaPoet or KotlinPoet.
💰 想测试你的市场判断力?我用 Polymarket 做预测市场交易——这是全球最大的预测市场平台,从大选结果到技术监管时间线,什么都可以押注。和赌博不同,这是真正的信息市场:你懂的信息越多,胜率越高。我靠预测技术相关事件的走向已经赚了不少。用我的邀请链接注册:Polymarket.com