enum class Command(private vararg val commands: String) {
@FieldEnrich("changeLanguages", "commands")
CHANGE_LANGUAGE;
}
enum class CreatingCarStep(private vararg val values: String) : AbstractStep<CreatingCarStep> {
@FieldEnrich("brand-name", "values")
BRAND_NAME {
override fun next() = MODEL
override fun previous() = BRAND_NAME
};
}
I have two enums with vararg property and @FieldEnrich annotation.
Annotation looks like
@Target(FIELD, CLASS)
@Retention(RUNTIME)
annotation class FieldEnrich(
val property: String,
val field: String
)
Annotation is processing by object
object FieldEnricher {
lateinit var configurationProvider: ConfigurationProvider
fun enrichClass(clazz: Class<*>) {
clazz.enumConstants.forEach {
enrichField(it, clazz)
}
}
private fun enrichField(enum: Any, clazz: Class<*>) {
val enumClass = enum::class.java
if (enumClass.isAnnotationPresent(FieldEnrich::class.java)) {
val fieldEnrich = enumClass.getAnnotation(FieldEnrich::class.java)
val values = configurationProvider.getProperty(fieldEnrich.property, Array<String>::class.java)
val field = clazz.declaredFields.firstOrNull { it.name == fieldEnrich.field }
field?.isAccessible = true
field?.set(enum, values)
}
}
}
The logic is as follows. We annotate an enum member with the annotation @FieldEnrich and pass the property we would like to read value from and the name of the field to which we set the value of the property.
I was debugging and found out when it was trying to process CreatingCarStep enum is okay, because the enumConstants method returns actual objects of the enum values. So i can just take the class of this value and get the actual class of this enum and process it by my method enrichField. But when it was trying to process Command enum, I got just enum values. So if we take the class of enum value, the same class of Command will be returned. Enter the image description here.
Command image -> enter image description here
CreatingCarStep image -> enter image description here
CodePudding user response:
It works for CreatingCarStep
because its enum constants have a non-empty body. This forces the kotlin compiler to create subclasses of the enum class for each of the enum constants. Additionally, the annotations on the enum constant will be put on the generated subclass. Therefore, enum::class.java
evaluates to a Class<*>
instance that represents a subclass of CreatingCarStep
that has a FieldEnrich
annotation.
When the enum constants have an empty body, or do not have a body at all, no subclasses are generated. The enum constants are instances of the enum class itself. Hence, enum::class.java
evaluates to Command::class.java
, which has no FieldEnrich
annotation.
Rather than getting clazz.enumConstants
and getting the annotations on their ::class.java
, you should get the annotations on the enum constant fields. This way, you do not depend on whether or not the enum constants have empty bodies.
fun enrichClass(clazz: Class<*>) {
clazz.declaredFields.forEach {
if (it.isEnumConstant) {
enrichField(it, clazz)
}
}
}
private fun enrichField(enumField: Field, clazz: Class<*>) {
if (enumField.isAnnotationPresent(FieldEnrich::class.java)) {
val fieldEnrich = enumField.getAnnotation(FieldEnrich::class.java)
val values = configurationProvider.getProperty(fieldEnrich.property, Array<String>::class.java)
val field = clazz.declaredFields.firstOrNull { it.name == fieldEnrich.field }
field?.isAccessible = true
field?.set(enumField.get(null), values)
}
}