天天看点

kotlin 注解声明与使用

前言

函数的调用需要知道函数所在的类,函数的入参个数和参数类型,注解和反射可以跳过这个限制,注解和反射可以编写事先未知的任意类代码。注解可以为类库赋予特定的语义,反射可以在运行时分析类的结构。

注解的声明与使用

注解允许你把额外的元数据关联到一个声明上,然后通过反射在运行时对元数据进行提取并使用。

要应用一个注解,以字符@作为名字前缀,放在注解前面。

@Test
fun add(){

}      

可以给注解提供实参

@Deprecated("removeAt(index) instead.",ReplaceWith("removeAt(index)"))
    fun remove(index:Int){
        
    }
    //RelaceWith 也是一个注解
    @Target()
    @Retention(BINARY)
    @MustBeDocumented
    public annotation class ReplaceWith(val expression: String, vararg val imports: String)      

注意:

注解的实参类型可以是基本类型、字符串、枚举、类引用、其他的注解类、数组等,在java中声明的注解类,命名为value的形参按需自动转换成的可变长度的形参,所以可以不用arrayOf函数就可以提供多个实参。

说明 表达式
把一个类指定为注解实参 @CustomAnnotation(MineClass::class)
另一个注解指定为实参 去掉注解的@,例如:ReplaceWith作为注解实参时,未加@前缀
把一个数组指定为实参 @RequestMapping(path=arrayof(“/foo”,“/bar”))

注解实参需要编译期确定,所以作为注解实参的属性需要使用const修饰,声明在文件的顶层或者object之中。

注解目标

说明元素中那些需要注解十分必要,因为kotlin源代码中的单个声明会对应成多个java声明元素。

{
var shengming=1
}
对应java
一个声明属性、一个getter,一个setter,编译时会自动生成这些模板代码      

使用点目标声明被用来说明要注解的元素,而在java中只需要对字段或者函数添加@Rule注解就可以。

@get:Rule      
  1. @get: 使用点目标
  2. Rule:注解名称
class foler{
@get:Rule
val folder=TemporaryFolder()
@Test
fun testDemo(){
...
}      

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

使用点目标 介绍
property java的注解下不能应用
field 字段
get 属性getter
set 属性setter
receiver 接收者参数属性
param 构造方法属性
setparam 属性setter的参数
delegate 委托属性存储委托实例的字段
file 文件中声明的顶层函数和属性的类

kotlin中允许对任意的表达式应用注解

Tips:用注解可以控制javaApi的生成

注解 解释
@volatile 充当java关键字volatile
@strictfp 充当java关键字strictfp
@JvmName 改变由kotlin生成的java方法或字段的名称
@JvmStatic 把对象声明或者伴生对象的方法上,暴露成java static静态方法
@JvmOverloads 为带默认参数生成多个重载
@JvmField 应用于属性,暴露成没有访问器public 字段

声明注解

//不带参数的注解
annotation class MineAnnotation
//带参数的注解
annotation class MineAnnotation(val name:String)      

注解类没有类主体,编译器也禁止。注解类用来关联到声明和表达式的元数据的结构,在java中声明同样的结构

public @interface MineAnnotation{
String value()
}      

java中value在kotlin和java中均会被特殊对待。当你应用一个注解时,需要提供除value以外所有指定特性的显示名称.而kotli’n中不需要这么做。和普通构造函数一样用法。如果将java中声明的注解应用到kotlin中,必须对除value以外的所有实参使用命名实参法

//Target.java

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}      

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

元注解:可以应用到注解类上的注解被称作元注解

@Target(AnnotationTarget.PROPERTY)
annotation class JsonExclude      

@Target元注解说明注解可以被应用的元素类型,AnnotationTarget 枚举值列出了可以应用注解的全部可能的目标,如果需要也可以同时声明多个AnnotationTarget目标

public enum class AnnotationTarget {
    /** Class, interface or object, annotation class is also included */
    CLASS,
    /** Annotation class only */
    ANNOTATION_CLASS,
    /** Generic type parameter (unsupported yet) */
    TYPE_PARAMETER,
    /** Property */
    PROPERTY,
    /** Field, including property's backing field */
    FIELD,
    /** Local variable */
    LOCAL_VARIABLE,
    /** Value parameter of a function or a constructor */
    VALUE_PARAMETER,
    /** Constructor only (primary or secondary) */
    CONSTRUCTOR,
    /** Function (constructors are not included) */
    FUNCTION,
    /** Property getter only */
    PROPERTY_GETTER,
    /** Property setter only */
    PROPERTY_SETTER,
    /** Type usage */
    TYPE,
    /** Any expression */
    EXPRESSION,
    /** File */
    FILE,
    /** Type alias */
    @SinceKotlin("1.1")
    TYPEALIAS
}      

如何定义自己的元注解:ANNOTATION_CLASS

@Target(AnnotationTarget.ANNOTATION_CLASS)
annotation class myAnnotation

使用
@myAnnotation
annotation class MYBinding      

注意: PROPERTY注解只在kotlin中可用,如果java中要可用需要同时声明

@Target(AnnotationTarget.FIELD,AnnotationTarget.PROPERTY),对于@Retention注解来声明注解是否会存储到.class文件,运行时是否可以通过反射类访问它,在java中会在.class保留但是无法运行时获取,在kotlin中默认为在.class中存储注解信息,在Runtime保留注解。

使用类作为注解参数

使用场景就是在对一个嵌套类进行反序列化时候,定制使用那个类型实参来进行赋值

class School{
 //学校名
 val schoolName
 //办公室,使用类名称::class 来引用一个类
 @DeserializeInterface(xxx::class)
 var office:Office
}      

注解的声明

annotation class DeserializeInterface(val targetClass:Kclass<out Any>)      

注意这里的泛型修饰符out的用法,表示保留子类型的关系。即协变,Kclass可以接收的类型包括Any即Any衍生出来的任何子类型都可以作为Kclass的泛型类型。如果不使用out进行修饰的话,则只能传递Any作为Kclass的泛型类型。

使用泛型类做注解

interface ValueSeriailizer<T>{

  fun toJsonValue(value:T):Any?
  
  fun fromJsonValue(jsonValue:Any?):T

}

data class Persion{

@CustomAnnotation(DateSerializer::class)
val date:Date
}

//@CustomAnnotation 如何声明
annotaion class CustomAnnotation(val serializerClass:Kclass<ValueSerializer<*>>)      

之后调用时候需要保证serializerClass调用时输入的实参需要实现ValueSerializer接口类

Kclass<out ValueSerializer<*>>      

out修饰符,表示接收任何实现了ValueSerializer接口,不只是ValueSerializer::class

ValueSerializer<*>中的 * ,可以序列化任何值

//如果YourClassName 有自己的实参类型,就用*代替
Kclass<out YourClassName>      

总结

  1. java中应用注解语法和kotlin几乎一摸一样
  2. kotlin让注解的目标范围比java更广,包括了文件和表达式
  3. 一个注解类的参数可以是基本类型、字符串、枚举、类引用、其他注解类实例、或者数据
  4. 使用点目标来处理kotlin这种一个声明产生多个字节码元素情况。var a=1 对应java中三种字节码元素。
  5. 注解类声明拥有一个主构造没有类主体构造方法中所有参数都被标示成val属性
  6. 元注解用来指定使用点目标、保留期模式、和其他注解的特性