Kotlin Help

Use annotation processors in Kotlin projects

Annotation processors analyze your source code at compile time to generate boilerplate code, validate usage, or produce other artifacts. Kotlin supports two ways to work with annotation processors:

  • The kapt compiler plugin works by generating stub files from Kotlin source code and then running the Java annotation processors on those stubs. This extra stub-generation step makes the build time slower and means kapt can't understand Kotlin-specific constructs, such as extension functions or null safety.

    kapt supports both Maven and Gradle. It's recommended for all Maven projects and for Gradle projects with processor libraries that haven't yet adopted KSP, such as MapStruct.

  • The KSP framework reads Kotlin source code directly through a Kotlin-first API, without generating stubs. It understands Kotlin-specific features natively and runs builds faster than kapt.

    Currently, KSP has official support only for Gradle. It's recommended for writing your own processors and working with KSP-compatible libraries like Dagger.

Use kapt with Java annotation processors

kapt lets you use existing Java annotation processors in Kotlin projects without any changes to the processors themselves.

The example below shows how to use the MapStruct annotation processor, which generates type-safe mapper implementations between Java beans at compile time.

  1. In your build file, apply the kapt plugin and add MapStruct to the dependencies section:

    <properties> <kotlin.compiler.jvmTarget>11</kotlin.compiler.jvmTarget> <mapstruct.version>1.6.3</mapstruct.version> </properties> <dependencies> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>${mapstruct.version}</version> </dependency> </dependencies> <plugin> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-maven-plugin</artifactId> <version>${kotlin.version}</version> <extensions>true</extensions> <executions> <execution> <id>kapt</id> <goals> <goal>kapt</goal> </goals> <configuration> <sourceDirs> <sourceDir>src/main/kotlin</sourceDir> <sourceDir>src/main/java</sourceDir> </sourceDirs> <aptMode>stubs</aptMode> <annotationProcessorPaths> <annotationProcessorPath> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${mapstruct.version}</version> </annotationProcessorPath> </annotationProcessorPaths> </configuration> </execution> </executions> </plugin>
    • Add the execution of the kapt goal from kotlin-maven-plugin before the compile execution.

    • Configure the level of annotation processing using the aptMode option.

    plugins { kotlin("kapt") version "2.3.21" } dependencies { implementation("org.mapstruct:mapstruct:1.6.3") kapt("org.mapstruct:mapstruct-processor:1.6.3") }
    plugins { id "org.jetbrains.kotlin.kapt" version "2.3.21" } dependencies { implementation "org.mapstruct:mapstruct:1.6.3" kapt "org.mapstruct:mapstruct-processor:1.6.3" }
  2. Define your data classes and a mapper interface:

    import org.mapstruct.Mapper import org.mapstruct.factory.Mappers data class UserDto(val id: Long, val firstName: String, val lastName: String) data class UserEntity(val id: Long, val firstName: String, val lastName: String) @Mapper interface UserMapper { fun toDto(entity: UserEntity): UserDto fun toEntity(dto: UserDto): UserEntity companion object : UserMapper by Mappers.getMapper(UserMapper::class.java) }
  3. Build the project. MapStruct generates the UserMapperImpl class in the generated sources' directory. Use the UserMapper companion object to call the generated implementation:

    fun main() { val entity = UserEntity(id = 1L, firstName = "John", lastName = "Doe") val dto = UserMapper.toDto(entity) println(dto) // UserDto(id=1, firstName=John, lastName=Doe) }

Use KSP in Gradle projects

With KSP, you can use existing annotation processors in Gradle projects and create your own processors that generate code based on annotations in your source code.

Use KSP with Java annotation processors

For Gradle projects, use KSP with the compatible annotation processors. KSP is faster than kapt and can understand Kotlin-specific features natively. See the list of libraries that already support KSP.

The example below shows how to use Dagger, a compile-time dependency injection framework that generates the wiring code for your dependency graph.

  1. In your build.gradle(.kts) file, apply the KSP plugin and add Dagger to the dependencies block:

    // build.gradle.kts plugins { kotlin("jvm") version "2.3.21" id("com.google.devtools.ksp") version "2.3.7" } dependencies { implementation("com.google.dagger:dagger:2.59.2") ksp("com.google.dagger:dagger-compiler:2.59.2") }
    // build.gradle plugins { id 'org.jetbrains.kotlin.jvm' version '2.3.21' id 'com.google.devtools.ksp' version '2.3.7' } dependencies { implementation 'com.google.dagger:dagger:2.59.2' ksp 'com.google.dagger:dagger-compiler:2.59.2' }

  2. Annotate your Kotlin classes with Dagger annotations:

    import javax.inject.Inject import javax.inject.Singleton import dagger.Component import dagger.Module import dagger.Provides @Singleton class UserRepository @Inject constructor() { fun getUser(): String = "John Doe" } @Module class AppModule { @Provides @Singleton fun provideUserRepository(): UserRepository = UserRepository() } @Singleton @Component(modules = [AppModule::class]) interface AppComponent { fun userRepository(): UserRepository }
  3. Build the project. Dagger generates implementation classes, such as DaggerAppComponent in the build/generated/ksp directory. Use the generated class in your code:

    fun main() { val appComponent = DaggerAppComponent.create() val userRepository = appComponent.userRepository() println("User: ${userRepository.getUser()}") // User: John Doe }

For more information on Dagger support for KSP, see its documentation.

Create your own annotation processor

You can use the KSP API to write your own annotation processors that generate code at compile time. A new processor requires three modules:

  • An annotation module that declares the custom annotation.

  • A processor module that implements the SymbolProcessor and SymbolProcessorProvider factories. SymbolProcessor contains the main logic, while SymbolProcessorProvider creates the processor and registers the provider in the META-INF/services/ path.

  • An app module that applies the KSP plugin, depends on the processor, and uses the annotation.

For complete step-by-step instructions, see the KSP quickstart.

What's next

07 May 2026