注解

    注解的附加属性可以通过用元注解标注注解类来指定:

    • 指定可以用该注解标注的元素的可能的类型(类、函数、属性、表达式等);
    • @Retention 指定该注解是否存储在编译后的 class 文件中,以及它在运行时能否通过反射可见 (默认都是 true);
    • 允许在单个元素上多次使用相同的该注解;
    • @MustBeDocumented 指定该注解是公有 API 的一部分,并且应该包含在生成的 API 文档中显示的类或方法的签名中。
    1. @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
    2. AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION)
    3. @Retention(AnnotationRetention.SOURCE)
    4. @MustBeDocumented
    5. annotation class Fancy
    1. @Fancy class Foo {
    2. @Fancy fun baz(@Fancy foo: Int): Int {
    3. return (@Fancy 1)
    4. }
    5. }

    如果需要对类的主构造函数进行标注,则需要在构造函数声明中添加 constructor 关键字 ,并将注解添加到其前面:

    1. class Foo @Inject constructor(dependency: MyDependency) { …… }

    你也可以标注属性访问器:

    1. class Foo {
    2. var x: MyDependency? = null
    3. @Inject set
    4. }

    注解可以有接受参数的构造函数。

    1. annotation class Special(val why: String)
    2. @Special("example") class Foo {}

    允许的参数类型有:

    • 对应于 Java 原生类型的类型(Int、 Long等);
    • 字符串;
    • 类(Foo::class);
    • 枚举;
    • 其他注解;
    • 上面已列类型的数组。

    注解参数不能有可空类型,因为 JVM 不支持将 null 作为注解属性的值存储。

    1. annotation class ReplaceWith(val expression: String)
    2. annotation class Deprecated(
    3. val replaceWith: ReplaceWith = ReplaceWith(""))
    4. @Deprecated("This function is deprecated, use === instead", ReplaceWith("this === other"))

    如果需要将一个类指定为注解的参数,请使用 Kotlin 类 ()。Kotlin 编译器会自动将其转换为 Java 类,以便 Java 代码能够正常访问该注解与参数 。

    1. import kotlin.reflect.KClass
    2. annotation class Ann(val arg1: KClass<*>, val arg2: KClass<out Any>)
    3. @Ann(String::class, Int::class) class MyClass

    注解也可以用于 lambda 表达式。它们会被应用于生成 lambda 表达式体的 invoke() 方法上。这对于像 Quasar 这样的框架很有用, 该框架使用注解进行并发控制。

    当对属性或主构造函数参数进行标注时,从相应的 Kotlin 元素生成的 Java 元素会有多个,因此在生成的 Java 字节码中该注解有多个可能位置 。如果要指定精确地指定应该如何生成该注解,请使用以下语法:

    1. @get:Ann val bar, // 标注 Java getter
    2. @param:Ann val quux) // 标注 Java 构造函数参数

    可以使用相同的语法来标注整个文件。 要做到这一点,把带有目标 file 的注解放在文件的顶层、package 指令之前或者在所有导入之前(如果文件在默认包中的话):

    1. @file:JvmName("Foo")
    2. package org.jetbrains.demo

    如果你对同一目标有多个注解,那么可以这样来避免目标重复——在目标后面添加方括号并将所有注解放在方括号内:

    1. class Example {
    2. @set:[Inject VisibleForTesting]
    3. var collaborator: Collaborator
    4. }

    支持的使用处目标的完整列表为:

    • file
    • property(具有此目标的注解对 Java 不可见);
    • field
    • get(属性 getter);
    • set(属性 setter);
    • receiver(扩展函数或属性的接收者参数);
    • param(构造函数参数);
    • setparam(属性 setter 参数);
    • delegate(为委托属性存储其委托实例的字段)。
    1. fun @receiver:Fancy String.myExtension() { ... }

    如果不指定使用处目标,则根据正在使用的注解的 @Target 注解来选择目标 。如果有多个适用的目标,则使用以下列表中的第一个适用目标:

    • param;
    • property;
    • field.

    Java 注解与 Kotlin 100% 兼容:

    1. import org.junit.Test
    2. import org.junit.Assert.*
    3. import org.junit.Rule
    4. import org.junit.rules.*
    5. class Tests {
    6. // 将 @Rule 注解应用于属性 getter
    7. @Test fun simple() {
    8. val f = tempFolder.newFile()
    9. assertEquals(42, getTheAnswer())
    10. }

    因为 Java 编写的注解没有定义参数顺序,所以不能使用常规函数调用语法来传递参数。相反,你需要使用具名参数语法:

    1. // Java
    2. public @interface Ann {
    3. int intValue();
    4. String stringValue();
    5. }
    1. // Kotlin
    2. @Ann(intValue = 1, stringValue = "abc") class C

    就像在 Java 中一样,一个特殊的情况是 value 参数;它的值无需显式名称指定:

    1. // Kotlin
    2. @AnnWithValue("abc") class C

    如果 Java 中的 value 参数具有数组类型,它会成为 Kotlin 中的一个 vararg 参数:

    1. // Java
    2. public @interface AnnWithArrayValue {
    3. String[] value();
    4. }
    1. // Kotlin
    2. @AnnWithArrayValue("abc", "foo", "bar") class C

    对于具有数组类型的其他参数,你需要显式使用数组字面值语法(自 Kotlin 1.2 起)或者 arrayOf(……)

    1. // Java
    2. public @interface AnnWithArrayMethod {
    3. String[] names();
    4. }
    1. // Kotlin 1.2+:
    2. @AnnWithArrayMethod(names = ["abc", "foo", "bar"])
    3. class C
    4. // 旧版本 Kotlin:
    5. @AnnWithArrayMethod(names = arrayOf("abc", "foo", "bar"))
    6. class D
    1. // Java
    2. public @interface Ann {
    3. int value();
    4. }
    1. // Kotlin
    2. fun foo(ann: Ann) {
    3. val i = ann.value