Kotlin 注解的使用目标(Use-site Target)问

2021-12-31  本文已影响0人  聂小强

Kotlin 注解的使用目标(Use-site Target)问题

在测试kotlin的注解的时候发现了些许问题

这里我假设了一个小需求 ,有一个User实体

dataclassUser(valage:Number,varname:String,vargender:String="")

再自定义一个注解

@Target(AnnotationTarget.FIELD,AnnotationTarget.PROPERTY)

@Retention(AnnotationRetention.RUNTIME)

annotationclassGender(

valvalue:String

)

目的是在实例化User的时候给User加一个Gender的注解,再通过反射把value值赋给user对象

于是开始代码

classAnnotationTest{

@Gender(value="男")

valuser2=User(20,"张三")

  @Test

funtestAnnotation() {

valuser=AnnotationTest::user2

valobj=user.get(this)

valgender=User::gender

valannotation=user.findAnnotation<Gender>()

  gender.set(obj,annotation2?.value?:"")

println(user2)

   }

}

运行后打印

User(age=20,name=张三,gender=男)

看起来没什么毛病

于是我又想使用java的反射方式尝试一下

@Test

funbuildUser() {

valfield=this@AnnotationTest::class.java.getDeclaredField("user2");

valannotations=field.declaredAnnotations

valobj=field.get(this)

for(annotationinannotations) {

if(annotationisGender) {

valvalue=annotation.value

println("注解value : $value")

valgenderField=obj::class.java.getDeclaredField("gender")

genderField.isAccessible=true

genderField.set(obj,value)

       }

   }

println(user2.toString())

}

查看一下打印结果

User(age=20,name=张三,gender=)

咦 ,什么情况 kotlin 注解不是号称兼容java注解的么,于是查找了下资料 .

发现kotlin的注解在使用的时候是需要指定(Use-site Target)的

file 包含在文件中声明的顶层函数和顶层属性注解, 比如`@file:JvmName("ClassName");

property (使用这个目标的注解, 在 Java 中无法访问);

field 相当于java的field;

get (属性的 get 方法);

set (属性的 set 方法);

receiver (扩展函数或扩展属性的接受者参数);

param (构造器的参数);

setparam (属性 set 方法的参数);

delegate (保存代理属性的代理对象实例的域变量);

并查到kotlin的注解如果不指定注解的使用目标(Use-site Target), 那么将会根据这个注解的 @Target 注解来自动选定使用目标. 如果存在多个可用的目标, 将会使用以下列表中的第一个(优先级由上至下):

param;

property;

field.

Bingo!! 问题出来了 我这里定义的Gender注解@target指定的是(AnnotationTarget.FIELD,AnnotationTarget.PROPERTY),

由于其中包含AnnotationTarget.PROPERTY,并且在使用@Gender的时候并没有指定注解目标(Use-site Target)

导致注解目标默认成property ,而property注解信息并不能在java的反射中获取到

于是解决方案就来了

第一种方案

去掉定义的Gender注解@target中的AnnotationTarget.PROPERTY.由于定义的时候排除掉了property 在使用的时候自然不需要再指定

@Target(AnnotationTarget.FIELD)

@Retention(AnnotationRetention.RUNTIME)

annotationclassGender(

valvalue:String

)

使用时(由于未使用kotlin的property属性 在获取注解的时候自然不能再通过 findAnnotation 获取到 ,这里需要使用javaField 获取)

classAnnotationTest{

@Gender(value="男")

valuser2=User(20,"张三")

  @Test

funtestAnnotation2() {

valuser=AnnotationTest::user2

valobj=user.get(this)

valgender=User::gender

valannotation=user.javaField?.getAnnotationsByType(Gender::class.java)

if(annotation?.isNotEmpty()==true) {

gender.set(obj,annotation[0].value?:"")

       }

println(user2)

   }

}

打印结果User(age=20,name=张三,gender=男)

第二种方案 使用注解时指定注解目标(Use-site Target),通过@目标:注解的语法

@Target(AnnotationTarget.FIELD,AnnotationTarget.PROPERTY)

@Retention(AnnotationRetention.RUNTIME)

annotationclassGender(

valvalue:String

)

使用时( 加上@field:Gender 表示只在 field上使用注解,  当然也可以使用kotlin的特性@property:Gender ,只不过这种方式并不兼容java)

classAnnotationTest{

@field:Gender(value="男")

valuser2=User(20,"张三")

  @Test

funtestAnnotation2() {

valuser=AnnotationTest::user2

valobj=user.get(this)

valgender=User::gender

valannotation=user.javaField?.getAnnotationsByType(Gender::class.java)

if(annotation?.isNotEmpty()==true) {

gender.set(obj,annotation[0].value?:"")

       }

println(user2)

   }

}

打印结果 User(age=20, name=张三, gender=男)

总结:

由于kotlin的属性(property)包含java中的field及setter和getter,在使用注解的时候会产生歧义,所以kotlin创造了注解目标(Use-site Target)以明确注解的作用目标,所以在不明确注解的@target的时候使用注解时尽量指明注解的使用目标(Use-site Target),否则可能会出现未知的问题

使用kotlin注解时如果未指定使用目标(Use-site Target),其默认的使用目标是根据注解的@target来决定的(这里是重点),而并不是固定的.其优先级顺序为 param ->  property ->  field

kotlin注解的@property:并不能在java中获取到 这是kotlin的特性,具体获取注解的方式如下

println(Example::foo.annotations)// 获取 @property: 修饰的注解

println(Example::foo.findAnnotation<Ann>())// 获取 @property: 修饰的注解

println(Example::foo.getter.annotations)// 获取 @get: 修饰的注解

println(Example::foo.getter.findAnnotation<Ann>())// 获取 @get: 修饰的注解

println(Example::foo.javaField?.annotations)// java方式获取注解信息,获取 @field: 修饰的注解以及原生java注解

println(Example::foo.javaField?.getAnnotation(Ann::class.java))// java方式获取注解信息,获取 @field: 修饰的注解以及原生java注解

在测试这个问题的时候遇到了一个无比巨坑,ide使用的是AndroidStudio,在调试代码的时候出现了各种各样奇葩的情况,比如@target只有AnnotationTarget.FIELD的情况下却获取不到java的注解信息 ,但能获取到kotlin的注解信息.再或者@target为两个,但未指定注解目标

却获取到了java的注解信息而获取不到kotlin的注解信息.种种如此很是奇葩,后经过调试和分析反编译成java的代码后发现,是由于编译缓存的原因,需要在修改之后 clean一下  或者随便修改一下代码 哪怕是加个注释 让编译器重新编译 问题解决

上一篇 下一篇

猜你喜欢

热点阅读