Kotlin注释:获取界面内部注释属性

时间:2020-03-18 15:53:22

标签: android kotlin annotations

概述

我正在尝试构建我的第一个注释处理器,并且进展顺利。我正在创建一个代码生成处理器,该处理器基本上为定义的接口生成SharedPreferences。我当前的注释是SharedPrefsDefault@SharedPrefs通知处理器该文件是接口,需要生成的prefs文件。 @Default是我在界面中注释了一些属性,以便让处理器知道将默认值设置为什么。可以有多个文件定义为@SharedPrefs

实施

我目前使用以下代码来获取用@SharedPrefs@Default注释的文件列表:

roundEnv.getElementsAnnotatedWith(SharedPrefs::class.java)?.forEach { element ->
  ...
  roundEnv.getElementsAnnotatedWith(Default::class.java)?.forEach {
    ...
  }
}

@SharedPrefs

@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.CLASS)
annotation class SharedPrefs(val showTraces: Boolean = false)

@Default

@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.PROPERTY)
annotation class Default(val defaultValue: String = "[null]")

使用中:

@SharedPrefs
interface InstanceSettings {
    var wifiPassword: String
    @Default("20")
    var count: Int
}

问题

按原样,内部forEach all 文件返回的 all 属性都用@Default注释。代码生成工作正常,但是这似乎不是最好的方法。有没有办法只获取我正在处理的当前@SharedPrefs类中的属性?例如,类似:

roundEnv.getElementsAnnotatedWith(SharedPrefs::class.java)?.forEach { element ->
  ...
  element.methodToGetAnnotatedProperties(Default::class.java)?.forEach {
    ...
  }
}

*编辑*

我发现,对于我注释的方法

@SomeAnnotation
fun someMethod()

我可以遍历element.enclosingElements并使用enclosingElement.getAnnotation(<SomeAnnotation::class.java>)查找它是否具有注释。不幸的是,如果我在这里错了,请纠正我,我无法用AnnotationTarget.FIELD注释接口变量,因为它们是接口并且没有实现,因此它们没有后备字段。因此,我正在使用AnnotationTarget.PROPERTY。当遍历封闭的元素时,所有变量都显示为getter和setter。对于上面InstanceSettings的示例,我得到了getWifiPasswordsetWifiPasswordgetCountsetCount。我仅获得wifiPasswordcount的元素。调用getAnnotation(Default::class.java)会始终返回null,因为它们已生成。

此外,任何人都知道的有关注释处理的任何其他资源都将成为注释中的重要补充。谢谢!

2 个答案:

答案 0 :(得分:0)

有人问过Java中类似的问题here

在这里,您可以创建扩展功能来为您工作!

roundEnv.getElementsAnnotatedWith(SharedPrefs::class.java)?.forEach { element ->
    ...
    roundEnv.findNestedElements(SharedPrefs::class, Default::class)?.forEach {
        ...
    }
}

fun <T> RoundEnvironment.findNestedElements(parent: KClass<*>, child: KClass<T>): List<Element>? {
    val childs = this.getElementsAnnotatedWith(child.java)
    val list = ArrayList<Element>()
    for (element in childs)
    {
        if (element.getEnclosingElement().getAnnotation(parent.java) != null)
        {
            list.add(element)
        }
    }
    return if(list.isEmpty()) null else list
}

答案 1 :(得分:0)

所以我想我已经找到了解决方法:

InterfaceSettings为例:

@SharedPrefs
interface InstanceSettings {
    var wifiPassword: String
    @Default("20")
    var count: Int
}

生成的简化Java代码是:

public interface InstanceSettings {
   @NotNull
   String getWifiPassword();

   void setWifiPassword(@NotNull String var1);

   int getCount();

   void setCount(int var1);

   public static final class DefaultImpls {
      public static void count$annotations() {
      }
   }
}

调用roundEnv.getElementsAnnotatedWith(Default::class.java)时返回的元素是count$annotations。因此,当我致电getEnclosingElement()时,将返回DefaultImpls。如果我再次致电getEnclosingElement(),则返回InstanceSettings。我实际上对该结果调用了getSimpleName(),并将其与带有@SharedPrefs的项中的className进行比较,以查看它是否是子项:

roundEnv.getElementsAnnotatedWith(SharedPrefs::class.java)?.forEach { element ->
  val className = element.simpleName.toString()
  val defaults = roundEnv.getElementsAnnotatedWith(Default::class.java)?.filter {
    // it.enclosingElement -> DefaultImpls
    // DefaultImpls.enclosingElement -> com.mypackage.InstanceSettings
    it.enclosingElement.enclosingElement.simpleName == className
  }
}