企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# 注解 [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 } ```