Kotlin logo

The Kotlin Blog

Kotlin Programming Language by JetBrains

Android Libraries Mobile Tools

Better Annotation Processing: Supporting Stubs in kapt

This post is largely outdated.
Please refer to the kapt documentation

We announced kapt, an Annotation Processing Tool for Kotlin, some time ago, and discussed its limitations. Now most of the limitations are going away with the updated version of kapt that is available as a 0.1-SNAPSHOT preview.

Recap: How kapt worked before

The initial version of kapt worked by intercepting communication between annotation processors (e.g. Dagger 2) and javac, and added already-compiled Kotlin classes on top of the Java classes that javac saw itself in the sources. The problem with this approach was that, since Kotlin classes had to be already compiled, there was no way for them to refer to any code generated by the processor (e.g. Dagger’s module classes). Thus we had to write Dagger application classes in Java.

How it works now

As discussed in the previous blog post, the problem can be overcome by generating stubs of Kotlin classes before running javac and then running real compilation after javac has finished. Stubs contain only declarations and no bodies of methods. The Kotlin compiler used to create such stubs in memory anyways (they are used for Java interop, when Java code refers back to Kotlin), so all we had to do was serialize them to files on disk.

Example: DBFlow

Stubs enable frameworks that rely on code generated by annotation processors. For example, you can now use DBFlow in Kotlin:

public object ItemRepository {

    public fun getAll(): MutableList<Item> {
        return Select()
                .orderBy(false, Item_Table.UPDATED_AT)


The DSL-like functions Select(), from() etc are provided by DBFLow library, and Item_Table is generated by DBFlow’s annotation processor, and the Kotlin code above can happily refer to it!

The full example is available here (thanks to Mickele Moriconi for the initial code).

Note that generating stubs requires relatively much work, because all declarations must be resolved, and sometimes knowing return types requires analysis of expression (bodies of functions or property initializers after the = sign). So, using stubs in kapt slows your build down somewhat. That’s why stubs are off by default, and to enable them you need to write the following in your build.gradle file:

kapt {
    generateStubs = true

Also, kapt can now take care of passing parameters to annotation processors. Here’s an example for the AndroidAnnotations library:

kapt {
    generateStubs = true
    arguments {
        arg("androidManifestFile", variant.outputs[0].processResourcesTask.manifestFile)

Source-retained annotations

As you might have noticed, we generate stubs as binary .class-files, not as .java sources. This is more convenient for a number of reasons:

  • we already generate the necessary bytes for a different purpose,
  • in a class file we can simply skip a body of a method, no need to generate a stub body that would make javac happy,
  • javac would compile the stub sources and generate class files that we’d need to remove later,
  • this way the old (fast) and the new (slower) modes of kapt use the same essential mechanisms.

But binary stubs have their own disadvantages:

  • if an annotation is marked by @Retention(RetentionPolicy.SOURCE) it is not present in the binaries,
  • javac does not propagate @Inherited annotations down the class hierarchies.

We have not addressed the latter issue so far, but the former, source-retained annotations is absolutely critical since many popular frameworks (such as DBFlow) have their annotations source-retained. Fortunately, when javac reads the binaries, it does not double-check the annotations, and if we write a source-retained annotation to the class file despite the declared retention, it will happily see it. This is what we do now 🙂

Remaining limitations

There’s some work still pending on kapt.

The biggest issue with annotation processing itself is supporting @Inherited annotations. We’ll need to work around javac not propagating them down the hierarchies of binary classes.

But the real issue lies outside kapt: many frameworks, such as AndroidAnnotation and the aforementioned DBFlow want to inject values into fields directly, and Kotlin being all about safety and making those fields private is getting in the way. This is why for now we have to write DBFlow “table classes” in Java, see Item.java in our example.

So, we are thinking of an opt-in functionality to make fields non-private in classes generated by Kotlin.


The new kapt is not released yet, but you are welcome to try it out and tell us what you think. Here’s an example of how you do it:

repositories {
    maven { url 'https://raw.github.com/Raizlabs/maven-releases/master/releases' }
    maven { url 'http://oss.sonatype.org/content/repositories/snapshots' }

dependencies {

    // DBFlow
    kapt 'com.raizlabs.android:DBFlow-Compiler:2.0.0'
    compile 'com.raizlabs.android:DBFlow-Core:2.0.0'
    compile 'com.raizlabs.android:DBFlow:2.0.0'

    // Kotlin
    compile 'org.jetbrains.kotlin:kotlin-stdlib:0.1-SNAPSHOT'

kapt {
    generateStubs = true

Again, see the full DBFlow example here.

Please tell us:

  • What worked for you?
  • What didn’t?
  • What do you like or dislike?
  • Any use cases we overlooked?


Comments below can no longer be edited.

18 Responses to Better Annotation Processing: Supporting Stubs in kapt

  1. Avatar

    Salomon BRYS says:

    June 22, 2015

    Great work 😉

    One thing that I am missing is the ability to generate Kotlin code with an annotation processor.

    • Avatar

      Andrey Breslav says:

      June 22, 2015

      This is unlikely to be supported in forseeable future

  2. Avatar

    Dale King says:

    June 22, 2015

    Regarding the visibility of backing fields are you talking about a switch that would make all fields non-private? Don’t like that idea.

    It should be tied to specific annotations. That could be a single additional annotation you add to the injected field to control access or what would be most helpful is to allow defining a mapping from individual annotations to an access level. So I could tell the compiler that any field annotated with javax.inject for example should be package access. The question would then be how to specify that mapping to the compiler.

    • Avatar

      Andrey Breslav says:

      June 22, 2015

      We are talking about a way of annotating individual properties to make their fields public/protected. E.g.:

      val foo: Int = 1
  3. Avatar

    Mickele Moriconi says:

    June 22, 2015

    Awesome! I can’t wait to try it out 🙂

  4. Avatar

    HE Guangyu says:

    June 25, 2015

    In my opinion, this implementation is not so elegant, and brings much complexity for users.

    Maybe we should accept the situation, “, there was no way for them to refer to any code generated by the processor (e.g. Dagger’s module classes)”, and hope that:
    1 some programer rewrite Dagger or DBFlow by Kotlin, and elemites these limitation.
    2 Kotlin team provides some guides on how to write annotation-processing-friendly programs for kotlin programmers.

    • Avatar

      Andrey Breslav says:

      June 25, 2015

      Since using kapt is totally optional, there’s no harm in having it, and your points keep being viable.

  5. Avatar

    Sven Jacobs says:

    July 11, 2015

    Great progress!

    I’m looking forward to the possibility of field injection in Kotlin with Dagger 2. At the moment you still have to write too much boilerplate code.

    What is the YouTrack issue regarding the non-private fields opt-in functionality?

    • Avatar

      Yan Zhulanow says:

      July 15, 2015

      @publicField annotation is already available in 0.1-SNAPSHOT version. Please give us a feedback if you use it!

      • Avatar

        Sven Jacobs says:

        July 15, 2015

        Works fine! 🙂

  6. Avatar

    Sven Jacobs says:

    July 14, 2015

    After a few days of experimenting with Kotlin, Android and Dagger 2 I have the following issue:

    When I perform a clean build and run the application (Cmd+C, Cmd+R) from Android Studio (v1.2.2) everything works fine but once I rerun the project (Cmd+R) without changing any file, the generated classes seem to be missing from the APK:

    java.lang.NoClassDefFoundError: Failed resolution of: L … DaggerApplicationComponent;

    Only when I perform a clean build the application runs from the IDE. This does not seem to happen when compiling via Gradle from command line.

    Any idea what this could be?

    • Avatar

      Yan Zhulanow says:

      July 15, 2015

      Could you send the sample project to yan.zhulanow[at]jetbrains.com?

    • Avatar

      Anatolii says:

      September 13, 2015

      Is there any solutuon for this issue?

      • Avatar

        Yan Zhulanow says:

        September 13, 2015

        This should be fixed in master now. Please check if all is ok with the ‘0.1-SNAPSHOT’ version.

  7. Avatar

    Cyril Vlaminck says:

    July 22, 2015

    After some times playing around with Kotlin, I tried integrating AndroidAnnotations as you show in this post. Unfortunately I did not manage to make AndroidAnnotations preprocessor process annotations on my Kotlin classes. They do work on Java classes in the project.

    As far as I understand, stub classes are generated and included in the classpath of the javac that will compile the Java sources and execute the annotation processor but stubs classes are not detected by AA processor.

    Do I miss something? Do you have a working exemple for AndroidAnnotations?

  8. Avatar

    Robbie Straw says:

    July 28, 2015

    This worked really well, I was able to move some JPA Entities over to Kotlin and run the QueryDSL APT by adding two lines to my build.gradle file.

    kapt 'com.querydsl:querydsl-apt:4.0.2:jpa'
    kapt 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final'

    My only complaint is that there doesn’t seem to be a way to instruct kapt to store the generated sources. So while my build works, the generated classes cannot be seen by my IDE and thus break autocomplete.

  9. Avatar

    Rondillor says:

    August 4, 2015

    I cannot manage to make Dagger2 work:
    Whatever I do it builds right the first time or when I do a clean (gradlew clean build). But when I rebuild a second time (gradlew build for example) I get the following exception:
    com.android.dex.DexException: Multiple dex files define Ljavax/inject/Inject;
    at com.android.dx.merge.DexMerger.readSortableTypes(DexMerger.java:596)
    at com.android.dx.merge.DexMerger.getSortedTypes(DexMerger.java:554)
    at com.android.dx.merge.DexMerger.mergeClassDefs(DexMerger.java:535)
    at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:171)
    at com.android.dx.merge.DexMerger.merge(DexMerger.java:189)
    at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:454)
    at com.android.dx.command.dexer.Main.runMonoDex(Main.java:303)
    at com.android.dx.command.dexer.Main.run(Main.java:246)
    at com.android.dx.command.dexer.Main.main(Main.java:215)
    at com.android.dx.command.Main.main(Main.java:106)

    But again this disappear each time I do a clean before… I never got these issues wti

Discover more