Edit Page

注解

注解的声明

注解是连接元数据以及代码的。为了声明注解,把annotation 这个关键字放在类前面:

annotation class Fancy

Additional attributes of the annotation can be specified by annotating the annotation class with meta-annotations:

  • @Target specifies the possible kinds of elements which can be annotated with the annotation (classes, functions, properties, expressions etc.);
  • @Retention specifies whether the annotation is stored in the compiled class files and whether it’s visible through reflection at runtime (by default, both are true);
  • @Repeatable allows using the same annotation on a single element multiple times;
  • @MustBeDocumented specifies that the annotation is part of the public API and should be included in the class or method signature shown in the generated API documentation.
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
        AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
public annotation class Fancy

用途

@Fancy class Foo {
  @Fancy fun baz(@Fancy foo: Int): Int {
    return (@Fancy 1)
  }
}

如果你需要注解类的主构造方法,你需要给构造方法的声明添加constructor这个关键字 ,还有在前面添加注解:

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

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

class Foo {
    var x: MyDependency? = null
        @Inject set
}

构造方法

注解可以有参数的构造方法。

annotation class Special(val why: String)

@Special("example") class Foo {}

Allowed parameter types are:

  • types that correspond to Java primitive types (Int, Long etc.);
  • strings;
  • classes (Foo::class);
  • enums;
  • other annotations;
  • arrays of the types listed above.

If an annotation is used as a parameter of another annotation, its name is not prefixed with the @ character:

public annotation class ReplaceWith(val expression: String)

public annotation class Deprecated(
        val message: String,
        val replaceWith: ReplaceWith = ReplaceWith(""))

@Deprecated("This function is deprecated, use === instead", ReplaceWith("this === other"))

If you need to specify a class as an argument of an annotation, use a Kotlin class (KClass). The Kotlin compiler will automatically convert it to a Java class, so that the Java code will be able to see the annotations and arguments normally.

import kotlin.reflect.KClass

annotation class Ann(val arg1: KClass<*>, val arg2: KClass<out Any?>)

@Ann(String::class, Int::class) class MyClass

Lambdas

注解也可以用在lambda表达式中。这将会应用到 lambda 生成的invoke()方法 。这对Quasar框架很有用, 在这个框架中注解被用来并发控制

annotation class Suspendable

val f = @Suspendable { Fiber.sleep(10) }

Annotation Use-site Targets

When you’re annotating a property or a primary constructor parameter, there are multiple Java elements which are generated from the corresponding Kotlin element, and therefore multiple possible locations for the annotation in the generated Java bytecode. To specify how exactly the annotation should be generated, use the following syntax:

class Example(@field:Ann val foo,    // annotate Java field
              @get:Ann val bar,      // annotate Java getter
              @param:Ann val quux)   // annotate Java constructor parameter

The same syntax can be used to annotate the entire file. To do this, put an annotation with the target file at the top level of a file, before the package directive or before all imports if the file is in the default package:

@file:JvmName("Foo")

package org.jetbrains.demo

If you have multiple annotations with the same target, you can avoid repeating the target by adding brackets after the target and putting all the annotations inside the brackets:

class Example {
     @set:[Inject VisibleForTesting]
     public var collaborator: Collaborator
}

The full list of supported use-site targets is:

  • file
  • property (annotations with this target are not visible to Java)
  • field
  • get (property getter)
  • set (property setter)
  • receiver (receiver parameter of an extension function or property)
  • param (constructor parameter)
  • setparam (property setter parameter)
  • delegate (the field storing the delegate instance for a delegated property)

To annotate the receiver parameter of an extension function, use the following syntax:

fun @receiver:Fancy String.myExtension() { }

If you don’t specify a use-site target, the target is chosen according to the @Target annotation of the annotation being used. If there are multiple applicable targets, the first applicable target from the following list is used:

  • param
  • property
  • field

Jave注解

Java注解是百分百适用于Kotlin:

import org.junit.Test
import org.junit.Assert.*
import org.junit.Rule
import org.junit.rules.*

class Tests {
  // apply @Rule annotation to property getter
  @get:Rule val tempFolder = TemporaryFolder()

  @Test fun simple() {
    val f = tempFolder.newFile()
    assertEquals(42, getTheAnswer())
  }
}

因为在Java里,注释的参数顺序不是明确的,你不能使用常规的方法 调用语法传递的参数。相反的,你需要使用指定的参数语法。

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

就像在Jave里一样,需要一个特殊的参数是’ value`参数;它的值可以使用不明确的名称来指定。

// Java
public @interface AnnWithValue {
    String value();
}
// Kotlin
@AnnWithValue("abc") class C

如果在Java中value参数是array类型,在Kotlin中必须使用 vararg这个参数。

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

For other arguments that have an array type, you need to use arrayOf explicitly:

// Java
public @interface AnnWithArrayMethod {
    String[] names();
}
// Kotlin
@AnnWithArrayMethod(names = arrayOf("abc", "foo", "bar")) class C

注解实例的值被视为Kotlin的属性。

// Java
public @interface Ann {
    int value();
}
// Kotlin
fun foo(ann: Ann) {
    val i = ann.value
}