Как вывести (java) аннотации на поля классов данных Kotlin? - PullRequest
0 голосов
/ 19 декабря 2018

Я использую аннотацию 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 */ }
}

На данный момент у меня сложилось впечатление, что эту аннотацию нужно как-то обнаружитьчерез котлинское отражение.Насколько я могу сказать, это не так.Я пытался повторить аннотации:

  1. Каждое поле конструктора класса данных Kotlin
  2. Каждое поле Kotlin
  3. Каждая функция Kotlin
  4. Каждая JavaКонструктор
  5. Каждое поле Java
  6. Каждый метод Java

Он просто нигде не отображается.

В тот момент, когда я изменяю использованиеаннотация, подобная этой (обратите внимание на целевой спецификатор "get" сейчас):

data class MyDataClass(
    @get:PropertyName("z") val x: Int
)

Аннотация теперь отображается в сгенерированном геттере объекта класса Java.Это, по крайней мере, на практике, но мне любопытно, почему Kotlin позволяет мне скомпилировать аннотацию в виде аннотации с таргетингом на поле, но не позволяет вернуть ее обратно во время выполнения (если я не пропустил что-то вAPI-интерфейсы kotlin-рефлекса?).

Если я вместо этого использую эту аннотацию на основе Kotlin:

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.PROPERTY)
annotation class PropertyName(val value: String)

При этом аннотация появляется во время выполнения на поле Kotlin.Это любопытно, потому что ElementType.FIELD в Java просто не может идеально отображаться в 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())
    }
}

Ответы [ 2 ]

0 голосов
/ 19 декабря 2018

Проблема здесь в том, что мои ожидания (и, возможно, документация) не подготовили меня к тому, что компилятор Kotlin будет делать с аннотациями различных типов.Я предполагал, что целевая цель аннотации FIELD для свойства класса данных Kotlin будет применять аннотацию непосредственно к синтетическому свойству Kotlin.Это предположение не соответствует действительности.

Что Kotlin будет делать с аннотацией FIELD для синтетического свойства, так это переместит аннотацию FIELD вниз к фактическому вспомогательному полю для свойства в созданном файле класса.Это означает, что любое отражение аннотированного свойства Kotlin не найдет аннотацию вообще. Вы должны обратиться к объекту Java Class, чтобы найти его.

Если вы хотите аннотировать свойство класса Kotlin и найти его с помощью отражения KClass, вы должны использовать PROPERTYТип аннотации, которая является уникальной для Kotlin.При этом, если вы найдете свойство в списке members KClass, оно будет иметь эту аннотацию (но не базовое вспомогательное поле!).

В дальнейшем, с классами данных Kotlin, конструктор будетсамая важная вещь, которая определяет свойства для класса.Поэтому, если вы хотите создать экземпляр класса данных с помощью отражения во время выполнения, лучше всего аннотировать его свойства с помощью конструктора.Это означает применение аннотации с типом VALUE_PARAMETER к свойствам конструктора класса данных, где они могут быть обнаружены путем отражения самих параметров конструктора.

В более общем смысле, типы аннотаций, которые определены в Java *Только 1014 * применяются к отражению класса Java, в то время как типы аннотаций, расширенных Kotlin only , применяются к отражению KClass.Компилятор Kotlin запретит вам использовать специфичные для Kotlin типы аннотаций для элементов Java.Исключением является то, что это позволит вам применять типы аннотаций Java к концепциям Kotlin (свойствам с полями поддержки), которые «сводятся» к нативным концепциям Java.(FWIW, если вы копируете собственный код аннотации Java в Kotlin и выполняете его автоматическое преобразование, преобразование может не иметь смысла без этого.)

Если ваша любимая библиотека Java предоставляет только аннотации, которые применяются к слою Javaконцепции, попросите их предоставить расширения Kotlin, которые помогут вам работать с их аннотациями на более чисто уровне Kotlin.Хотя это может быть сложно использовать в коде Java.

Кто-то, пожалуйста, обновите документы.: -)

0 голосов
/ 19 декабря 2018

Хотя я могу найти его в Kotlin KClass, я могу найти его в Java.

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
annotation class PropertyName(val value: String)

data class MyDataClass(
    @PropertyName("z") val x: Int
)

И я использую следующий код

val a = MyDataClass(1)
a::class.java.declaredFields.forEach {
    it.annotations.forEach { annotation ->
        Log.e(it.name, annotation.toString())
    }
}

Он печатает

2018-12-19 11: 33: 07.663 25318-25318 / com.example.application E / x: @ com.example.PropertyName (значение = z)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...