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一下 或者随便修改一下代码 哪怕是加个注释 让编译器重新编译 问题解决
网友评论