Kotlin进阶知识(十二)——声明并应用注解

1,319 阅读3分钟

一、应用注解

在Kotlin中使用注解的方法和Java一样。要应用一个注解,以**@字符作为(注解)名字的前缀**,并放在要注解的声明最前面

注解只能拥有如下类型的参数:基本数据类型字符串枚举类引用其他的注解类,以及前面这些类型数组

指定注解实参的语法与Java有些微小的差别:

  • 要把一个类指定为注解实参,在类名后加上**::class: @MyAnnotation(MyClass::class)**。
  • 要把另一个注解指定为一个实参去掉注解名称前面的@
  • 要把一个数组指定为一个实参,使用**arrayOf函数**:@RequestMapping(path = arrayOf("/foo", "/bar"))

注解实参需要在编译期就是已知的,所以不能引用任意的属性作为实参。要把属性当作注解实参使用,需要用**const修饰符标记它,来告知编译器这个属性编译期变量**。

const val TEST_TIMEOUT = 100L

@Test(timeout = TEST_TIMEOUT) fun testMethod() { ... }

二、注解目标

使用点目标声明被用来说明要注解的元素。使用点目标被放在**@符号注解名称之间,并用冒号和注解名称隔开**,如图1。

图1:说明使用点目标的语法

class HasTempFolder {
    // 注解的是getter,而不是属性。
    @get: Rule
    val folder = TemporaryFolder()

    @Test
    fun testUsingTempFolder() {
        // ...
    }
}

注:上述代码需使用JUnit框架

使用Java中声明的注解来注解一个属性,它会被默认地应用到相应的字段上。Kotlin也可以让你声明被直接对应到属性上的注解。

Kotlin支持的使用点目标的完整列表如下:

  • property:Java的注解不能应用这种使用点目标。
  • field:为属性生成的字段
  • get:属性的getter
  • set: 属性的setter
  • receiver扩展函数或者扩展属性接受者参数
  • param构造方法的参数
  • setparam: **属性setter**的参数
  • delegate: 为委托属性存储委托实例的字段
  • file: 包含在文件中声明的顶层函数和属性的类

注意:任何应用到file目标的注解都必须放在文件的顶层,放在**package指令之前**。

用注解控制JavaAPI Kotlin提供了各种注解来控制Kotlin编写的声明如何编译成字节码并暴露给Java调用者。

  • @JvmName 将改变由**Kotlin生成的Java方法或字段的名称**
  • @JvmStatic 能被用在对象声明或者伴生对象方法上,把它们暴露成**Java的静态方法**
  • @JvmOverloads 指导Kotlin编译器为带默认参数值的函数生成多个重载(函数)
  • @JvmField 可以应用于一个属性,把这个属性暴露成一个没有访问器的公有Java字段

三、声明注解

annotation class JsonExclude

语法看起来和常规类的声明很像,只是在class关键字之前加上了**annotation修饰符**。因为注解类只是用来定义关联到声明和表达式元数据的结构,它们不能包含**任何代码**。因此编译器禁止为一个注解类指定类主体。

四、元注解:控制如何处理一个注解

和Java一样,一个Kotlin注解类自己也可以被注解。可以应用到注解类上的注解被称作元注解。

标准库定义的元注解中最常见的就是@Target。JKid中@JsonExclude和@JsonName的声明使用它为这些注解指定有效的目标。

@Target(AnnotationTarget.PRIPERTY)
annotation class JsonExclude

注:@JsonExclude 是 JKid库中的方法

@Target元注解说明了注解可以被应用的元素类型。如果不使用它所有的声明都可以应用这个注解。这并不是JKid想要的,因为它只需要处理属性的注解。

五、使用泛型类做注解参数

@CustomSerializer注解接受一个自定义序列化器类的引用作为实参

interface ValueSerializer<T> {
    fun toJsonValue(value: T): Any?
    fun fromJsonValue(jsonValue: Any?): T
}

data class PersonAnnotation(
    val name: String,
    @CustomSerializer(DateSerializer::class) val birthDate: Date
)

// @CustomSerializer底层源码
annotation class CustomSerializer(
    val serializerClass: KClass<out ValueSerializer<*>>
)