【问题标题】:Kotlin Annotation: Get interface inner annotated propertiesKotlin Annotation:获取接口内部注释属性
【发布时间】:2020-03-20 00:22:44
【问题描述】:

概述

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

实施

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

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 正在从带有@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,因为它们是生成的。

此外,任何其他人都知道的关于注释处理的资源将是 cmets 中的一个很好的补充。谢谢!

【问题讨论】:

    标签: android kotlin annotations


    【解决方案1】:

    herehere提出了类似的 java 问题

    这里是你如何创建一个可以为你工作的扩展函数!

    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
    }
    

    【讨论】:

    • element.getEnclosingElement().getAnnotation(parent.java) 行返回DefaultImpls。我相信这是因为getElementsAnnotatedWith(Default::class.java) 正在返回生成的getter/setter 而不是支持字段。见我上面的编辑
    • 抱歉,对上述评论进行了小幅更正:element.getEnclosingElement() 返回DefaultImpls。调用 getAnnotation(parent.java) 返回 null(如预期的那样)。
    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-06-09
      • 2023-02-21
      • 1970-01-01
      • 2018-05-08
      • 2017-10-22
      • 1970-01-01
      • 2019-04-20
      • 1970-01-01
      相关资源
      最近更新 更多