注解声明(Annotation Declaration)
注解是将元数据附加到代码的手段。 要声明注解,请将annotation
修饰符放在类的前面:
annotation class Fancy
注解的附加属性可以通过使用元注解类来指定:
-
@Target
指定可以用该注解标注的元素的可能的类型(类、函数、属性、表达式等) -
@Retention
指定该注解是否存储在编译后的class文件中,以及它在运行时是否通过反射可见(默认都是true) -
@Repeatable
注解允许在单个元素上多次使用相同的该注解 -
@MustBeDocumented
表明该注解是公有API的一部分,并且应该包含在生成的API文档中显示的类或方法的签名中
如下:
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
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 {}
合法的参数类型有:
- 原生类型
- 字符串
- 类
- 枚举
- 其他注解
- 上述类型的数组
注解类型不能有可null类型,因为JVM不支持将null作为注解属性的值存储。
如果注解用作另一个注解的参数,则其名称不以@字符为前缀:
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类。Kotlin编译器会自动将其转换为Java类,以便Java代码能够正常看到该注解和参数。
import kotlin.reflect.KClass
annotation class Ann(val arg1: KClass<*>, val arg2: KClass<out Any?>)
@Ann(String::class, Int::class) class MyClass
λ表达式
注解也可以用于λ表达式。它们将被应用于λ表达式方法体生成的invoke()
方法。这对于像Quasar
这样的框架很有用,该框架使用注解进行并发控制:
annotation class Suspendable
val f = @Suspendable { Fiber.sleep(10) }
明确注解目标(Annotation Use-site Targets)
当对一个属性或一个主构造器的参数进行注解时,Kotlin元素将会生成对应的多个Java元素,因此在Java字节码中该注解有多个可能位置。如果要精确指定该如何生成该注解,请使用以下语法:
class Example(@field:Ann val foo, // annotate Java field
@get:Ann val bar, // annotate Java getter
@param:Ann val quux) // annotate Java constructor parameter
可以使用相同的语法来标注整个文件。要做到这一点,把带有目标file
的注解放在文件的顶层:package指令之前或在所有导入之前(如果文件在默认包中的话):
如果对同一目标有多个注解,可以使用下述方法来避免目标重复:在目标后面添加方括号并将所有注解放在方括号内:
class Example {
@set:[Inject VisibleForTesting]
var collaborator: Collaborator
}
支持使用明确注解目标的完整列表是:
- 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)
要标注扩展函数的接收者参数,请使用以下语法:
fun @receiver:Fancy String.myExtension() { }
如果你没有指明目标使用处,通过正在使用的@Target注解来选择目标。如果有多个适用的目标,则使用以下列表中第一个可用的:
- param
- property
- field
Java注解
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
和Java一样,特殊情况是value
参数;它的值无需显式名称指定。
// Java
public @interface AnnWithValue {
String value();
}
// Kotlin
@AnnWithValue("abc") class C
如果在Java中value
参数的类型是数组,则它在Kotlin中将是vararg
参数:
// Java
public @interface AnnWithArrayValue {
String[] value();
}
// Kotlin
@AnnWithArrayValue("abc", "foo", "bar") class C
对于具有数组类型的其他参数,需要显式使用arrayOf
:
// 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
}
网友评论