我使用 Firestore 的基于 Java 的注释来标记字段和将文档字段映射到 Java 类元素的方法:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface PropertyName {
String value();
}
我在 Kotlin 数据类中的字段上使用它,编译得很好:
data class MyDataClass(
@PropertyName("z") val x: Int
)
在 IntelliJ 和 Android Studio 中,我可以看到它显示在反编译的类转储中:
public final data class MyDataClass public constructor(x: kotlin.Int) {
@field:com.google.cloud.firestore.annotation.PropertyName public final val x: kotlin.Int /* compiled code */
public final operator fun component1(): kotlin.Int { /* compiled code */ }
}
我目前的印象是,这个注释应该可以通过 Kotlin 反射以某种方式发现。据我所知,事实并非如此。我尝试过迭代注释:
- 每个 Kotlin 数据类构造函数字段
- 每个 Kotlin 字段
- 每个 Kotlin 函数
- 每个Java构造函数
- 每个Java字段
- 每个Java方法
它只是没有出现在任何地方。
当我像这样更改注释的用法时(请注意现在的目标说明符“get”):
data class MyDataClass(
@get:PropertyName("z") val x: Int
)
该注释现在显示在生成的 Java 类对象的 getter 中。这至少在实践中是可行的,但我很好奇为什么 Kotlin 允许我将注释编译为面向字段的注释,但不允许我在运行时将其返回(除非我在kotlin-reflect API?)。
如果我改用这个基于 Kotlin 的注释:
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.PROPERTY)
annotation class PropertyName(val value: String)
这样,注释就会在运行时显示在 Kotlin 字段上。这很奇怪,因为 Java 的 ElementType.FIELD 似乎并没有完美地映射到 Kotlin 的 AnnotationTarget.FIELD。
(顺便说一句,如果我将其更改为 AnnotationTarget.VALUE_PARAMETER,我还可以在数据类构造函数参数中发现此注释。)
对我来说,这感觉像是一个错误,但我愿意看看我是否在这里做错了什么。或者这可能只是不受支持。我正在使用 Kotlin 1.3.11。 JVM 和 Android 上的行为相同。
查找注释的代码:
Log.d("@@@@@", "\n\nDump of $kclass")
val ctor = kclass.constructors.first()
Log.d("@@@@@", "Constructor parameters")
ctor.parameters.forEach { p ->
Log.d("@@@@@", p.toString())
Log.d("@@@@@", p.annotations.size.toString())
p.annotations.forEach { a ->
Log.d("@@@@@", " " + a.annotationClass)
}
}
Log.d("@@@@@", "kotlin functions")
kclass.functions.forEach { f ->
Log.d("@@@@@", f.toString())
if (f.annotations.isNotEmpty()) {
Log.d("@@@@@", "*** " + f.annotations.toString())
}
}
Log.d("@@@@@", "kotlin members")
kclass.members.forEach { f ->
Log.d("@@@@@", f.toString())
if (f.annotations.isNotEmpty()) {
Log.d("@@@@@", "*** " + f.annotations.toString())
}
}
Log.d("@@@@@", "kotlin declared functions")
kclass.declaredFunctions.forEach { f ->
Log.d("@@@@@", f.toString())
if (f.annotations.isNotEmpty()) {
Log.d("@@@@@", "*** " + f.annotations.toString())
}
}
val t = kclass.java
Log.d("@@@@@", "java constructors")
t.constructors.forEach { f ->
Log.d("@@@@@", f.toString())
}
Log.d("@@@@@", "java methods")
t.methods.forEach { f ->
Log.d("@@@@@", f.toString())
if (f.annotations.isNotEmpty()) {
Log.d("@@@@@", "*** " + f.annotations.toString())
}
}
Log.d("@@@@@", "java fields")
t.fields.forEach { f ->
Log.d("@@@@@", f.toString())
if (f.annotations.isNotEmpty()) {
Log.d("@@@@@", "*** " + f.annotations.toString())
}
}