【发布时间】:2021-04-01 20:55:23
【问题描述】:
假设我有一个财产
@MyAnnotation
class Foo {
var bar: List<String> = emptyList()
}
我正在为上面的代码编写注释处理器:
class AnnotationProcessor : AbstractProcessor() {
override fun getSupportedAnnotationTypes(): MutableSet<String> = mutableSetOf(...)
@KotlinPoetMetadataPreview
override fun process(annotations: MutableSet<out TypeElement>?, roundEnv: RoundEnvironment?
): Boolean {
val barPropType: TypeMirror = (roundEnv!!.getElementsAnnotatedWith(Command::class.java) as TypeElement)
.kotlinProperties
.first()
.type
return true
}
}
我现在想验证属性的类型是否可以从List<String> 分配
我能够找到的解决方案是使用TypeUtils 中可用的isAssignable 方法,但为了做到这一点,我需要一个TypeMirror for List 实例,但我无法找到一种方法做。我可以获得一个 List 实例并执行擦除以获得原始类型的 TypeMirror,但这对我没有帮助,因为它可以分配给任何 List<>,例如 List<Integer>:
val listType = processingEnv.elementUtils.getTypeElement(List::class.java.name).asType()
println((listType as DeclaredType).typeArguments) // E
println(processingEnv.typeUtils.isAssignable(barPropType, listType)) // false
println(processingEnv.typeUtils.isAssignable( // true, but also true when barPropType is List<Integer>
processingEnv.typeUtils.erasure(barPropType),
listType,
))
This talk提出以下解决方案:
/**
* Get the type parameter for a {@link Collection}.
* @param type a parameterized collection type
* @return the type of elements in the collection
*/
private DeclaredType getCollectionType(TypeMirror type) {
if (type != null && typeUtils().isAssignable(type, collectionType.type)) {
// This is a bit of a hack; to work properly, we should walk up the inheritance hierarchy, tracking type
// parameters as we go. For example, if the type in question is StringList, which implements List<String>,
// then this code would not work.
List<? extends TypeMirror> typeArguments = ((DeclaredType) type).getTypeArguments();
return (DeclaredType) typeArguments.get(0);
}
return null;
}
对于我的项目,我需要那个“合适的”解决方案,但我不知道如何实施。我做过这样的事情:
fun TypeElement.allSuperInterfaces(env: ProcessingEnvironment): List<TypeMirror> {
val result: MutableList<TypeMirror> = mutableListOf(this.asType())
result.addAll(interfaces)
var toCheckFurther = interfaces
while(toCheckFurther.isNotEmpty()) {
toCheckFurther = toCheckFurther
.mapNotNull { env.typeUtils.asElement(it) as? TypeElement }
.flatMap { it.interfaces }
.toMutableList()
result.addAll(toCheckFurther)
}
return result;
}
这样做的问题是,在从TypeMirror 到TypeElement 的转换过程中,有关实际类型参数的信息会丢失:
((barPropType as DeclaredType).asElement() as TypeElement)
.allSuperInterfaces(processingEnv)
.forEach(::println)
输出:
java.util.List<E>
java.util.Collection<E>
java.lang.Iterable<E>
但与此同时,似乎需要进行这种转换,因为无法从 TypeMirror 获取已实现的接口(至少我知道)。
实现这个涉及继承层次结构的解决方案的正确方法是什么?
【问题讨论】:
-
所以你有一个带有特定注释
@MyAnnotation的类,你想编写一个处理器来做什么?该类中的所有变量都可以分配给List<String>?对不起,但我很困惑(主要是因为我对 kotlin 知之甚少) -
我只是想验证一下
List<>的具体类型,例如List,某个TypeMirror。这种类型的镜像在上面的示例中表示为 barPropType。
标签: java kotlin javac annotation-processing