美文网首页
Kotlin 注解的使用目标(Use-site Target)问

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

作者: 聂小强 | 来源:发表于2021-12-31 15:27 被阅读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一下  或者随便修改一下代码 哪怕是加个注释 让编译器重新编译 问题解决

相关文章

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

    Kotlin 注解的使用目标(Use-site Target)问题 在测试kotlin的注解的时候发现了些许问题 ...

  • Kotlin注解

    定义注解 注解使用 Kotlin的元注解 @Target:定义注解能够应用于哪些目标对象 (CLASS,FUNCT...

  • Java注解

    基础注解Override WebServlet 元注解 @Target 注解的作用目标@Retention 注解的...

  • Java注解与Kotlin的兼容性

    Java注解与Kotlin完全兼容,只是在使用时略加注意即可。 一、指定注解的作用目标 为注解指定作用目标的语法格...

  • JAVA元注解@interface详解(@Target,@Doc

    jdk1.5起开始提供了4个元注解,用来定义自定义注解的注解,它们分别是: @Target 指定注解使用的目标范围...

  • Spring 常用注解解析

    原生注解 @Retention: 定义注解的保留策略 @Target: 定义注解的作用目标 @Document: ...

  • Kotlin注解入门

    Kotlin使用annotation class关键字来定义注解。 一、定义注解 Kotlin不允许为注解定义注解...

  • Kotlin使用注解

    Kotlin的反射也提供了一些支持注解的API。 一、提取注解信息 Kotlin使用kotlin.Annotion...

  • Android 常用注解

    Nullness注解 资源注解 资源注解的Target都是PARAMETER 类型定义注解@IntDef 使用情景...

  • Java注解

    元注解 @Target @Retention @Documented @Inherited 使用

网友评论

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

      本文链接:https://www.haomeiwen.com/subject/mnjajttx.html