# 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