我试图通过反射设置枚举字段

I tried to set enum field by reflection

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
  };
}

我有两个带有 vararg 属性 和 @FieldEnrich 注释的枚举。

注释看起来像

@Target(FIELD, CLASS)
@Retention(RUNTIME)
annotation class FieldEnrich(
  val property: String,
  val field: String
)

注释正在按对象处理

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)
    }
  }
}

逻辑如下。我们使用注解@FieldEnrich 对枚举成员进行注解,并传递我们希望从中读取值的 属性 以及我们设置 属性.

值的字段的名称

我正在调试并发现它在尝试处理 CreatingCarStep 枚举时没问题,因为枚举值的 enumConstants 方法 returns 实际对象。所以我可以只取这个值的 class 并得到这个枚举的实际 class 并通过我的方法 enrichField 处理它。但是当它试图处理 Command 枚举时,我得到的只是枚举值。因此,如果我们采用枚举值的 class,将返回与命令相同的 class。在此处输入图片描述。

命令图片-> enter image description here

CreatingCarStep 图像 -> enter image description here

它适用于 CreatingCarStep,因为它的枚举常量有一个 non-empty 主体。这会强制 kotlin 编译器为每个枚举常量创建枚举 class 的子classes。此外,枚举常量上的注释将放在生成的 subclass 上。因此,enum::class.java 求值为 Class<*> 实例,表示 CreatingCarStep 的子 class 具有 FieldEnrich 注释。

当枚举常量有一个空主体,或者根本没有主体时,不会生成子class。枚举常量是枚举 class 本身的实例。因此,enum::class.java 的计算结果为 Command::class.java,它没有 FieldEnrich 注释。

与其获取 clazz.enumConstants 并获取其 ::class.java 上的注释,不如获取枚举常量 字段 上的注释。这样,您就不会依赖于枚举常量是否有空体。

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)
    }
}