# 注解
[TOC]
## 注解聲明
**注解是將元數據附加到代碼的方法**。要聲明注解,請將**annotation修飾符**放在類的前面:
```kotlin
annotation class Fancy
```
**注解的附加屬性可以通過用元注解標注注解類來指定**:
* [`@Target`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.annotation/-target/index.html) 指定可以用該注解標注的元素的可能的類型(類、函數、屬性、表達式等);注解目標:指定該注解用于什么類型的元素(類,函數,屬性,表達式等);
* [`@Retention`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.annotation/-retention/index.html) 指定該注解是否存儲在編譯后的 class 文件中,以及它在運行時能否通過反射可見(默認都是 true)。
* [`@Repeatable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.annotation/-repeatable/index.html) 允許在單個元素上多次使用相同的該注解;
* [`@MustBeDocumented`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.annotation/-must-be-documented/index.html) 指定該注解是公有 API 的一部分,并且應該包含在生成的 API文檔中顯示的類或方法的簽名中。
```kotlin
@Target(AnnotationTarget.CLASS, //用于類
AnnotationTarget.FUNCTION, //用于函數
AnnotationTarget.VALUE_PARAMETER, //用于函數參數
AnnotationTarget.EXPRESSION) //用于表達式
@Retention(AnnotationRetention.SOURCE) //表示注解只存在源碼,不在編譯后的class文件
@MustBeDocumented //包含在API文檔中
annotation class Fancy
```
### 用法
```kotlin
@Fancy class Foo {
@Fancy fun baz(@Fancy foo: Int): Int {
return (@Fancy 1)
}
}
```
**如果需要對類的主構造函數進行標注,則需要在構造函數聲明中添加constructor關鍵字,并將注解添加到其前面**:
```kotlin
class Foo @Inject constructor(dependency: MyDependency) { …… }
```
你也可以標注屬性訪問器(get/set):
```kotlin
class Foo {
var x: MyDependency? = null
@Inject set
}
```
對Lambda表達式進行注解:
```
annotation class Suspendable
val f = @Suspendable { Fiber.sleep(10) }
```
### 構造函數
**注解可以有接受參數的構造函數**。
```kotlin
//聲明注解
annotation class Special(val why: String)
//使用注解
@Special("example") class Foo {}
```
kotlin注解允許的參數類型有:
* 對應于 Java 原生類型的類型(Int、 Long等);
* 字符串;
* 類(`Foo::class`);
* 枚舉;
* 其他注解;
* 上面已列類型的數組。
>[info]注意:注解參數不能有可空類型,因為 JVM 不支持將 `null` 作為注解屬性的值存儲。
**如果注解用作另一個注解的參數,則其名稱不以 @ 字符為前綴**:
```kotlin
annotation class ReplaceWith(val expression: String)
annotation class Deprecated(
val message: String,
val replaceWith: ReplaceWith = ReplaceWith(""))
@Deprecated("This function is deprecated, use === instead", ReplaceWith("this === other"))
```
**如果需要將一個類指定為注解的參數,請使用 Kotlin 類**([KClass](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-class/index.html))。**Kotlin 編譯器會自動將其轉換為 Java 類,以便 Java 代碼能夠正常看到該注解及參數**。
```kotlin
import kotlin.reflect.KClass
annotation class Ann(val arg1: KClass<*>, val arg2: KClass<out Any>)
@Ann(String::class, Int::class) class MyClass
```
### Lambda 表達式
注解也可以用于 lambda 表達式。它們會被應用于生成lambda 表達式體的 `invoke()`方法上。這對于像 [Quasar](http://www.paralleluniverse.co/quasar/) 這樣的框架很有用,該框架使用注解進行并發控制。
```kotlin
annotation class Suspendable
val f = @Suspendable { Fiber.sleep(10) }
```
## 注解使用處目標
當對屬性或主構造函數參數進行標注時,從相應的 Kotlin 元素生成的 Java 元素會有多個,因此在生成的 Java 字節碼中該注解有多個可能位置。說白了就是,**主構造函數參數(有val/var)就是kotlin類屬性,而[kotlin類屬性] = [Java類字段+get方法+set方法],因此在kotlin源碼編譯成Java字節碼時,該注解可能同時在多個元素上**!如果要精確地指定應該如何生成該注解,請使用以下語法:
1、對屬性元素(set/get/field/param)進行精確注解:
```kotlin
class Example(@field:Ann val foo, // 標注 Java 字段
@get:Ann val bar, // 標注 Java getter方法注解
@param:Ann val quux) // 標注 Java 構造函數參數
```
2、可使用相同的語法來標注注解整個文件。 要做到這一點,把帶有目標 `file` 的注解放在文件的頂層、package 指令之前或者在所有導入之前(如果文件在默認包中的話):
```kotlin
@file:JvmName("Foo")
package org.jetbrains.demo
```
注解的完整列表:
```
@file 注解整個文件, 放在文件頂部(package指令之前)
property 具有此目標的注解對Java不可見
field 字段,Java字段注解
get 屬性的getter方法,對Java get方法注解
set 屬性的setter方法,對Java set方法注解
receiver 擴展函數或屬性的接收者參數
param 構造函數參數
setparam 屬性的setter方法參數
delegate 為委托屬性存儲其委托實例對象的字段
```
**3、如果你對同一目標有多個注解,那么可以這樣來避免目標重復——在目標后面添加方括號并將所有注解放在方括號內,空格分隔**:
```kotlin
class Example {
@set:[Inject VisibleForTesting]
var collaborator: Collaborator
}
```
支持的使用處目標的完整列表為:
* `file`;
* `property`(具有此目標的注解對 Java 不可見);
* `field`;
* `get`(屬性 getter);
* `set`(屬性 setter);
* `receiver`(擴展函數或屬性的接收者參數);
* `param`(構造函數參數);
* `setparam`(屬性 setter 參數);
* `delegate`(為委托屬性存儲其委托實例的字段)。
4、要標注擴展函數的接收者參數,請使用以下語法:
```kotlin
fun @receiver:Fancy String.myExtension() { ... }
```
如果不指定使用處目標,則根據正在使用的注解的 `@Target` 注解來選擇目標。如果有多個適用的目標,則使用以下列表中的第一個適用目標:
* `param`;
* `property`;
* `field`.
## Java 注解
Java 注解與 Kotlin 100% 兼容:
```kotlin
// 導入Java注解
import org.junit.Test
import org.junit.Assert.*
import org.junit.Rule
import org.junit.rules.*
// kotlin代碼,使用java注解
class Tests {
// 將 @Rule 注解應用于屬性 getter
@get:Rule val tempFolder = TemporaryFolder()
// @Test注解用于simple方法/函數
@Test fun simple() {
val f = tempFolder.newFile()
assertEquals(42, getTheAnswer())
}
}
```
因為 Java 編寫的注解沒有定義參數順序,所以不能使用常規函數調用語法來傳遞參數。相反,你**需要使用命名參數語法**:
``` java
// Java代碼,聲明注解Ann
public @interface Ann {
int intValue();
String stringValue();
}
```
```kotlin
// Kotlin代碼,命名參數傳遞注解參數
@Ann(intValue = 1, stringValue = "abc") class C
```
就像在 Java 中一樣,一個特殊的情況是 `value` 參數;它的值無需顯式名稱指定:
``` java
// Java
public @interface AnnWithValue {
String value();
}
```
```kotlin
// Kotlin
@AnnWithValue("abc") class C
```
### 數組作為注解參數
如果 Java 中的 `value` 參數具有數組類型,它會成為 Kotlin 中的一個 `vararg` 參數(參數個數可變):
``` java
// Java
public @interface AnnWithArrayValue {
String[] value();
}
```
```kotlin
// Kotlin,參數類型是vararg(參數個數可變),相當于數組
@AnnWithArrayValue("abc", "foo", "bar") class C
```
對于具有數組類型的其他參數,你需要顯式使用數組字面值語法(自 Kotlin 1.2 起)或者`arrayOf(……)`:
``` java
// Java
public @interface AnnWithArrayMethod {
String[] names();
}
```
```kotlin
// Kotlin 1.2+:
@AnnWithArrayMethod(names = ["abc", "foo", "bar"])
class C
// 舊版本 Kotlin:
@AnnWithArrayMethod(names = arrayOf("abc", "foo", "bar"))
class D
```
### 訪問注解實例的屬性
**注解實例的值會作為屬性暴露給 Kotlin 代碼**:
``` java
// Java
public @interface Ann {
int value();
}
```
```kotlin
// Kotlin
fun foo(ann: Ann) {
val i = ann.value
}
```
- 前言
- 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