# Kotlin 注解处理
[TOC]
> 译注:kapt 即 Kotlin annotation processing tool(Kotlin 注解处理工具)缩写。
在 Kotlin 中通过 *kapt* 编译器插件支持注解处理器(参见[JSR 269](https://jcp.org/en/jsr/detail?id=269))。
简而言之,你可以在 Kotlin 项目中使用像 [Dagger](https://google.github.io/dagger/) 或者 [Data Binding](https://developer.android.com/topic/libraries/data-binding/index.html) 这样的库。
关于如何将 *kapt* 插件应用于 Gradle/Maven 构建中,请阅读下文。
## 在 Gradle 中使用
应用 `kotlin-kapt` Gradle 插件:
```groovy
//groovy
plugins {
id "org.jetbrains.kotlin.kapt" version "{{ site.data.releases.latest.version }}"
}
```
```kotlin
//kotlin
plugins {
kotlin("kapt") version "{{ site.data.releases.latest.version }}"
}
```
或者使用 `apply plugin` 语法:
```groovy
//groovy
apply plugin: 'kotlin-kapt'
```
然后在 `dependencies` 块中使用 `kapt` 配置添加相应的依赖项:
```groovy
//groovy
dependencies {
kapt 'groupId:artifactId:版本'
}
```
```kotlin
//kotlin
dependencies {
kapt("groupId:artifactId:版本")
}
```
如果你以前使用 [Android 支持](https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html#annotationProcessor_config)作为注解处理器,那么以 `kapt` 取代 `annotationProcessor` 配置的使用。如果你的项目包含 Java 类,`kapt` 也会顾全到它们。
如果为 `androidTest` 或 `test` 源代码使用注解处理器,那么相应的 `kapt` 配置名为 `kaptAndroidTest` 和 `kaptTest`。请注意 `kaptAndroidTest` 和 `kaptTest` 扩展了 `kapt`,所以你可以只提供 `kapt` 依赖而它对生产和测试源代码都可用。
## 注解处理器参数
使用 `arguments {}` 块将参数传给注解处理器:
```groovy
//groovy
kapt {
arguments {
arg("key", "value")
}
}
```
## Gradle 构建缓存支持(自 1.2.20 起)
默认情况下,kapt 注解处理任务就会[在 Gradle 中缓存](https://guides.gradle.org/using-build-cache/)。注解处理器所运行的任意代码可能不一定将输入转换为输出、可能访问与修改 Gradle 未跟踪的文件等。If the annotation processors used in the build cannot be properly cached, it is possible to disable caching for kapt entirely by adding the following lines to the build script, in order to avoid false-positive cache hits for the kapt tasks:
```groovy
kapt {
useBuildCache = false
}
```
## 并行运行 kapt 任务(自 1.2.60 起)
To improve the speed of builds that use kapt, you can enable the [Gradle worker API](https://guides.gradle.org/using-the-worker-api/) for kapt tasks.
Using the worker API lets Gradle run independent annotation processing tasks from a single project in parallel, which in some cases significantly decreases the execution time.
However, running kapt with Gradle worker API enabled can result in increased memory consumption due to parallel execution.
To use the Gradle worker API for parallel execution of kapt tasks, add this line to your `gradle.properties` file:
```
kapt.use.worker.api=true
```
## kapt 的避免编译(自 1.3.20 起)
To improve the times of incremental builds with kapt, it can use the Gradle [compile avoidance](https://docs.gradle.org/current/userguide/java_plugin.html#sec:java_compile_avoidance).
With compile avoidance enabled, Gradle can skip annotation processing when rebuilding a project. Particularly, annotation processing is skipped when:
* The project's source files are unchanged.
* The changes in dependencies are [ABI](https://en.wikipedia.org/wiki/Application_binary_interface) compatible. For example, the only changes are in method bodies.
However, compile avoidance can't be used for annotation processors discovered in the compile classpath since _any changes_ in them require running the annotation processing tasks.
To run kapt with compile avoidance:
* Add the annotation processor dependencies to the `kapt*` configurations manually as described [above](http://www.kotlincn.net/docs/reference/kapt.html#%E5%9C%A8-gradle-%E4%B8%AD%E4%BD%BF%E7%94%A8).
* Turn off the discovery of annotation processors in the compile classpath by adding this line to your `gradle.properties` file:
```
kapt.include.compile.classpath=false
```
## 增量注解处理(自 1.3.30 起)
Starting from version 1.3.30, kapt supports incremental annotation processing as an experimental feature.
Currently, annotation processing can be incremental only if all annotation processors being used are incremental.
To enable incremental annotation processing, add this line to your `gradle.properties` file:
```
kapt.incremental.apt=true
```
Note that incremental annotation processing requires [incremental compilation](http://www.kotlincn.net/docs/reference/using-gradle.html#%E5%A2%9E%E9%87%8F%E7%BC%96%E8%AF%91) to be enabled as well.
## Java 编译器选项
Kapt 使用 Java 编译器来运行注解处理器。以下是将任意选项传给 javac 的方式:
```groovy
kapt {
javacOptions {
// 增加注解处理器的最大错误次数
// 默认为 100。
option("-Xmaxerrs", 500)
}
}
```
## 非存在类型校正
一些注解处理器(如 `AutoFactory`)依赖于声明签名中的精确类型。默认情况下,Kapt 将每个未知类型(包括生成的类的类型)替换为 `NonExistentClass`,但你可以更改此行为。将额外标志添加到 `build.gradle` 文件以启用在存根(stub)中推断出的错误类型:
```groovy
kapt {
correctErrorTypes = true
}
```
## 在 Maven 中使用
在 `compile` 之前在 kotlin-maven-plugin 中添加 `kapt` 目标的执行:
```xml
<execution>
<id>kapt</id>
<goals>
<goal>kapt</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>src/main/kotlin</sourceDir>
<sourceDir>src/main/java</sourceDir>
</sourceDirs>
<annotationProcessorPaths>
<!-- 在此处指定你的注解处理器。 -->
<annotationProcessorPath>
<groupId>com.google.dagger</groupId>
<artifactId>dagger-compiler</artifactId>
<version>2.9</version>
</annotationProcessorPath>
</annotationProcessorPaths>
</configuration>
</execution>
```
你可以在[Kotlin 示例版本库](https://github.com/JetBrains/kotlin-examples/tree/master/maven/dagger-maven-example) 中找到一个显示使用 Kotlin、Maven 和 Dagger 的完整示例项目。
请注意,IntelliJ IDEA 自身的构建系统目前还不支持 kapt。当你想要重新运行注解处理时,请从“Maven Projects”工具栏启动构建。
## 在命令行中使用
Kapt 编译器插件已随 Kotlin 编译器的二进制发行版分发。可以使用 kotlinc 选项 `Xplugin` 提供该 JAR 文件的路径来附加该插件:
```bash
-Xplugin=$KOTLIN_HOME/lib/kotlin-annotation-processing.jar
```
以下是可用选项的列表:
* `sources`(*必需*):所生成文件的输出路径。
* `classes`(*必需*):所生成类文件与资源的输出路径。
* `stubs`(*必需*):存根文件的输出路径。换句话说,一些临时目录。
* `incrementalData`:二进制存根的输出路径。
* `apclasspath`(*可重复*):注解处理器 JAR 包路径。如果有的多个 JAR 包就传多个 `apclasspath` 选项。
* `apoptions`:注解处理器选项的 base64 编码列表。详见 [AP/javac options encoding](#apjavac-选项编码)。
* `javacArguments`:传给 javac 的选项的 base64 编码列表。详见 [AP/javac options encoding](#apjavac-选项编码)。
* `processors`:逗号分隔的注解处理器全类名列表。如果指定,kapt 就不会尝试在 `apclasspath` 中查找注解处理器。
* `verbose`:启用详细输出。
* `aptMode`(*必需*)
* `stubs`——只生成注解处理所需的存根;
* `apt`——只运行注解处理;
* `stubsAndApt`——生成存根并运行注解处理。
* `correctErrorTypes`:参见[下文](#在-gradle-中使用)。默认未启用。
插件选项格式为:`-P plugin:<plugin id>:<key>=<value>`。选项可以重复。
一个示例:
```bash
-P plugin:org.jetbrains.kotlin.kapt3:sources=build/kapt/sources
-P plugin:org.jetbrains.kotlin.kapt3:classes=build/kapt/classes
-P plugin:org.jetbrains.kotlin.kapt3:stubs=build/kapt/stubs
-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=lib/ap.jar
-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=lib/anotherAp.jar
-P plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true
```
## 生成 Kotlin 代码
Kapt 可生成 Kotlin 代码。是将生成的 Kotlin 源文件写入`processingEnv.options["kapt.kotlin.generated"]` 所指定的目录,这些文件会与主源代码一起编译。
可以在 [kotlin-examples](https://github.com/JetBrains/kotlin-examples/tree/master/gradle/kotlin-code-generation) Github 版本库中找到完整的示例。
请注意,对于所生成 Kotlin 文件,Kapt 不支持多轮处理。
## AP/javac 选项编码
`apoptions` 与 `javacArguments` 命令行选项接受选项编码映射。这是自己编码选项的方式:
```kotlin
fun encodeList(options: Map<String, String>): String {
val os = ByteArrayOutputStream()
val oos = ObjectOutputStream(os)
oos.writeInt(options.size)
for ((key, value) in options.entries) {
oos.writeUTF(key)
oos.writeUTF(value)
}
oos.flush()
return Base64.getEncoder().encodeToString(os.toByteArray())
}
```
- 前言
- Kotlin简介
- IntelliJ IDEA技巧总结
- idea设置类注释和方法注释模板
- 像Android Studion一样创建工程
- Gradle
- Gradle入门
- Gradle进阶
- 使用Gradle创建一个Kotlin工程
- 环境搭建
- Androidstudio平台搭建
- Eclipse的Kotlin环境配置
- 使用IntelliJ IDEA
- Kotlin学习路线
- Kotlin官方中文版文档教程
- 概述
- kotlin用于服务器端开发
- kotlin用于Android开发
- kotlin用于JavaScript开发
- kotlin用于原生开发
- Kotlin 用于数据科学
- 协程
- 多平台
- 新特性
- 1.1的新特性
- 1.2的新特性
- 1.3的新特性
- 开始
- 基本语法
- 习惯用法
- 编码规范
- 基础
- 基本类型
- 包与导入
- 控制流
- 返回与跳转
- 类与对象
- 类与继承
- 属性与字段
- 接口
- 可见性修饰符
- 扩展
- 数据类
- 密封类
- 泛型
- 嵌套类
- 枚举类
- 对象
- 类型别名
- 内嵌类
- 委托
- 委托属性
- 函数与Lambda表达式
- 函数
- Lambda表达式
- 内联函数
- 集合
- 集合概述
- 构造集合
- 迭代器
- 区间与数列
- 序列
- 操作概述
- 转换
- 过滤
- 加减操作符
- 分组
- 取集合的一部分
- 取单个元素
- 排序
- 聚合操作
- 集合写操作
- List相关操作
- Set相关操作
- Map相关操作
- 多平台程序设计
- 平台相关声明
- 以Gradle创建
- 更多语言结构
- 解构声明
- 类型检测与转换
- This表达式
- 相等性
- 操作符重载
- 空安全
- 异常
- 注解
- 反射
- 作用域函数
- 类型安全的构造器
- Opt-in Requirements
- 核心库
- 标准库
- kotlin.test
- 参考
- 关键字与操作符
- 语法
- 编码风格约定
- Java互操作
- Kotlin中调用Java
- Java中调用Kotlin
- JavaScript
- 动态类型
- kotlin中调用JavaScript
- JavaScript中调用kotlin
- JavaScript模块
- JavaScript反射
- JavaScript DCE
- 原生
- 并发
- 不可变性
- kotlin库
- 平台库
- 与C语言互操作
- 与Object-C及Swift互操作
- CocoaPods集成
- Gradle插件
- 调试
- FAQ
- 协程
- 协程指南
- 基础
- 取消与超时
- 组合挂起函数
- 协程上下文与调度器
- 异步流
- 通道
- 异常处理与监督
- 共享的可变状态与并发
- Select表达式(实验性)
- 工具
- 编写kotlin代码文档
- 使用Kapt
- 使用Gradle
- 使用Maven
- 使用Ant
- Kotlin与OSGI
- 编译器插件
- 编码规范
- 演进
- kotlin语言演进
- 不同组件的稳定性
- kotlin1.3的兼容性指南
- 常见问题
- FAQ
- 与Java比较
- 与Scala比较(官方已删除)
- Google开发者官网简介
- Kotlin and Android
- Get Started with Kotlin on Android
- Kotlin on Android FAQ
- Android KTX
- Resources to Learn Kotlin
- Kotlin样品
- Kotlin零基础到进阶
- 第一阶段兴趣入门
- kotlin简介和学习方法
- 数据类型和类型系统
- 入门
- 分类
- val和var
- 二进制基础
- 基础
- 基本语法
- 包
- 示例
- 编码规范
- 代码注释
- 异常
- 根类型“Any”
- Any? 可空类型
- 可空性的实现原理
- kotlin.Unit类型
- kotlin.Nothing类型
- 基本数据类型
- 数值类型
- 布尔类型
- 字符型
- 位运算符
- 变量和常量
- 语法和运算符
- 关键字
- 硬关键字
- 软关键字
- 修饰符关键字
- 特殊标识符
- 操作符和特殊符号
- 算术运算符
- 赋值运算符
- 比较运算符
- 逻辑运算符
- this关键字
- super关键字
- 操作符重载
- 一元操作符
- 二元操作符
- 字符串
- 字符串介绍和属性
- 字符串常见方法操作
- 字符串模板
- 数组
- 数组介绍创建及遍历
- 数组常见方法和属性
- 数组变化以及下标越界问题
- 原生数组类型
- 区间
- 正向区间
- 逆向区间
- 步长
- 类型检测与类型转换
- is、!is、as、as-运算符
- 空安全
- 可空类型变量
- 安全调用符
- 非空断言
- Elvis操作符
- 可空性深入
- 可空性和Java
- 函数
- 函数式编程概述
- OOP和FOP
- 函数式编程基本特性
- 组合与范畴
- 在Kotlin中使用函数式编程
- 函数入门
- 函数作用域
- 函数加强
- 命名参数
- 默认参数
- 可变参数
- 表达式函数体
- 顶层、嵌套、中缀函数
- 尾递归函数优化
- 函数重载
- 控制流
- if表达式
- when表达式
- for循环
- while循环
- 循环中的 Break 与 continue
- return返回
- 标签处返回
- 集合
- list集合
- list集合介绍和操作
- list常见方法和属性
- list集合变化和下标越界
- set集合
- set集合介绍和常见操作
- set集合常见方法和属性
- set集合变换和下标越界
- map集合
- map集合介绍和常见操作
- map集合常见方法和属性
- map集合变换
- 集合的函数式API
- map函数
- filter函数
- “ all ”“ any ”“ count ”和“ find ”:对集合应用判断式
- 别样的求和方式:sumBy、sum、fold、reduce
- 根据人的性别进行分组:groupBy
- 扁平化——处理嵌套集合:flatMap、flatten
- 惰性集合操作:序列
- 区间、数组、集合之间转换
- 面向对象
- 面向对象-封装
- 类的创建及属性方法访问
- 类属性和字段
- 构造器
- 嵌套类(内部类)
- 枚举类
- 枚举类遍历&枚举常量常用属性
- 数据类
- 密封类
- 印章类(密封类)
- 面向对象-继承
- 类的继承
- 面向对象-多态
- 抽象类
- 接口
- 接口和抽象类的区别
- 面向对象-深入
- 扩展
- 扩展:为别的类添加方法、属性
- Android中的扩展应用
- 优化Snackbar
- 用扩展函数封装Utils
- 解决烦人的findViewById
- 扩展不是万能的
- 调度方式对扩展函数的影响
- 被滥用的扩展函数
- 委托
- 委托类
- 委托属性
- Kotlin5大内置委托
- Kotlin-Object关键字
- 单例模式
- 匿名类对象
- 伴生对象
- 作用域函数
- let函数
- run函数
- with函数
- apply函数
- also函数
- 标准库函数
- takeIf 与 takeUnless
- 第二阶段重点深入
- Lambda编程
- Lambda成员引用高阶函数
- 高阶函数
- 内联函数
- 泛型
- 泛型的分类
- 泛型约束
- 子类和子类型
- 协变与逆变
- 泛型擦除与实化类型
- 泛型类型参数
- 泛型的背后:类型擦除
- Java为什么无法声明一个泛型数组
- 向后兼容的罪
- 类型擦除的矛盾
- 使用内联函数获取泛型
- 打破泛型不变
- 一个支持协变的List
- 一个支持逆变的Comparator
- 协变和逆变
- 第三阶段难点突破
- 注解和反射
- 声明并应用注解
- DSL
- 协程
- 协程简介
- 协程的基本操作
- 协程取消
- 管道
- 慕课霍丙乾协程笔记
- Kotlin与Java互操作
- 在Kotlin中调用Java
- 在Java中调用Kotlin
- Kotlin与Java中的操作对比
- 第四阶段专题练习
- 朱凯Kotlin知识点总结
- Kotlin 基础
- Kotlin 的变量、函数和类型
- Kotlin 里那些「不是那么写的」
- Kotlin 里那些「更方便的」
- Kotlin 进阶
- Kotlin 的泛型
- Kotlin 的高阶函数、匿名函数和 Lambda 表达式
- Kotlin协程
- 初识
- 进阶
- 深入
- Kotlin 扩展
- 会写「18.dp」只是个入门——Kotlin 的扩展函数和扩展属性(Extension Functions / Properties)
- Kotlin实战-开发Android