skip to content

What is kapt and what problem does the kotlin-kapt Gradle plugin solve for Kotlin projects?

level: juniorimportance: must knowfreq 60%

answer

  1. Kotlin Annotation Processing Tool
  2. Java stubs first, then process
  3. javax.annotation.processing.Processor
  4. kapt(...) configuration, kotlin-kapt plugin
  5. Bridge to Dagger/Room/Moshi

basics

~20 s

kapt is a Kotlin plugin that lets old Java annotation-processing tools (like Dagger or Room) work on Kotlin code. It does this by first turning Kotlin into simple Java placeholder files those tools can read.

solid answer

~40 s

kapt (Kotlin Annotation Processing Tool) is the `kotlin-kapt` Gradle plugin that runs standard Java `javax.annotation.processing.Processor` implementations against Kotlin sources. Java's `apt`/`Processor` API only understands Java type elements, not Kotlin, so kapt first generates Java **stubs** — empty-bodied Java declarations that mirror your Kotlin classes' signatures — then feeds those stubs to the annotation processors. Processors read annotations and emit generated code (e.g. Dagger's component classes, Room's DAO implementations). You apply the plugin and declare processors with the `kapt("...")` dependency configuration instead of `annotationProcessor`. It exists because, before KSP, the entire JVM annotation-processing ecosystem was Java-only and kapt was the bridge that made Dagger, Room, Moshi-codegen, etc. usable from Kotlin.

code

kotlin · 10 lines
kotlin
plugins {
    kotlin("jvm") version "2.1.0"
    kotlin("kapt") version "2.1.0"
}

dependencies {
    // Java annotation processor consumed via the kapt configuration
    kapt("com.google.dagger:dagger-compiler:2.51")
    implementation("com.google.dagger:dagger:2.51")
}

go deeper

for a junior

Knows kapt lets Java annotation processors work on Kotlin and is applied via the kotlin-kapt plugin.

for a middle

Explains the stub-generation step and the kapt(...) dependency configuration versus annotationProcessor(...).

for a senior

Frames kapt as the legacy bridge to the Java javax.annotation.processing ecosystem and can name the phases (stub gen, process, compile).

for a principal

Discusses why the design is inherently costly and positions kapt against KSP for ecosystem/migration decisions.

## What kapt is **kapt** stands for *Kotlin Annotation Processing Tool*. It is the `kotlin-kapt` Gradle plugin that allows **Java annotation processors** to run against Kotlin code. ## Background terms - **Annotation processor**: a compile-time plugin implementing `javax.annotation.processing.Processor` that reads annotations (e.g. `@Inject`, `@Entity`) and generates new source files. This is the `apt` mechanism built into `javac`. - **`javac`**: the Java compiler. Annotation processing is part of the Java compilation pipeline; processors see your code as Java **`Element`** objects (a `TypeElement` for a class, `ExecutableElement` for a method, etc.). - **The core problem**: that `Element` model only describes **Java** sources. The Kotlin compiler (`kotlinc`) is a separate compiler, so a Java processor cannot directly read Kotlin classes. ## How kapt bridges the gap — stub generation kapt works in phases: 1. **Stub generation**: kapt invokes the Kotlin compiler to produce **Java stub files** — `.java` files containing the public API surface (classes, method signatures, fields, annotations) of your Kotlin code but with **empty/placeholder method bodies** (typically `throw new RuntimeException("Stub!")`). These stubs are valid Java that `javac` and Java processors can read. 2. **Annotation processing**: kapt runs the registered Java processors over those stubs. Processors emit generated `.java` (and sometimes `.kt`) files. 3. **Compilation**: the generated sources plus your real Kotlin sources are compiled. ```kotlin // build.gradle.kts plugins { kotlin("jvm") version "2.1.0" kotlin("kapt") version "2.1.0" // applies the kotlin-kapt plugin } dependencies { kapt("com.google.dagger:dagger-compiler:2.51") // processor on the kapt classpath implementation("com.google.dagger:dagger:2.51") } ``` The `kapt("...")` configuration is the kapt equivalent of Java's `annotationProcessor("...")` configuration — it places the processor on the annotation-processing classpath. ## Why it matters Before **KSP** (Kotlin Symbol Processing), kapt was the *only* way to use the huge Java annotation-processing ecosystem — **Dagger/Hilt, Room, Moshi-codegen, AutoValue, Glide** — from Kotlin. It is now considered **legacy** because that stub-generation step is slow, but it remains widely used where no KSP processor exists. ## Key keywords - Plugin id: `kotlin-kapt` (DSL: `kotlin("kapt")`). - Dependency configuration: `kapt(...)`. - The defining mechanism: **Java stub generation** then standard `javax.annotation.processing`.

  • Why can't a Java annotation processor just read Kotlin classes directly?
    Java's annotation-processing API models code as Java `Element`s produced by `javac`. Kotlin is compiled by `kotlinc`, so there are no Java `Element`s for processors to inspect until kapt synthesizes Java stubs.
  • Which Gradle configuration adds a processor under kapt?
    The `kapt("group:artifact:version")` configuration, analogous to Java's `annotationProcessor(...)`.

kapt is a translator: Java processors only speak Java, so kapt writes a quick Java summary (stubs) of your Kotlin so the Java-only tools can read it.

saying these in an interview costs you the question

  • Claiming kapt is a separate compiler rather than a Gradle plugin around kotlinc + Java processing
  • Saying kapt runs Kotlin-native processors (those are KSP)
  • Not mentioning Java stub generation at all
  • Confusing kapt(...) with implementation(...) for the processor dependency

context