简体   繁体   English

Kotlin - 如何从注释处理器获取 KClass<*> 注释参数

[英]Kotlin - How to get KClass<*> annotation parameter from annotation processor

I have the following annotation:我有以下注释:

@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation class Model(
    val owner: KClass<*>,
    val consumer: KClass<*>
)

@Model(DataOwner::class, DataConsumer::class)
interface Student {
    val name: String
    val group: Group
}

I need to get the value of owner and consumer in my annotation processor.我需要在我的注释处理器中获取ownerconsumer的价值。

I've tried this approach:我试过这种方法:

private inline fun <reified T> findAnnotationValue(
    element: Element,
    annotationClass: KClass<*>,
    valueName: String
): T? {
    return element.annotationMirrors.map {
        it to it.annotationType.asElement() as TypeElement
    }.firstOrNull { (_, element) ->
        element.qualifiedName.contentEquals(annotationClass.qualifiedName)
    }?.let { (mirror, _) ->
        extractValue(mirror, valueName)
    }
}

private inline fun <reified T> extractValue(
    annotationMirror: AnnotationMirror,
    valueName: String
): T? {
    return annotationMirror.elementValues.toList()
        .firstOrNull { (key, _) ->
            key.simpleName.contentEquals(valueName)
        }?.let { (_, value) ->
            value.value as T
        }
}


val ownerClass: KClass<*> = findAnnotationValue(
    element,
    Model::class,
    "owner"
)

But it gave me this error:但它给了我这个错误:

e: [kapt] An exception occurred: java.lang.ClassCastException: com.sun.tools.javac.code.Type$ClassType cannot be cast to kotlin.reflect.KClass

I also tried this:我也试过这个:

val ownerClass: KClass<*> = element.getAnnotation(Model::class.java).owner

But it gave me this error:但它给了我这个错误:

e: [kapt] An exception occurred: javax.lang.model.type.MirroredTypeException: Attempt to access Class object for TypeMirror inc.ahmedmourad.systems.tutors.domain.model.DataOwner

inc.ahmedmourad.systems.tutors.domain.model.DataOwner is the owner value passed to the annotation. inc.ahmedmourad.systems.tutors.domain.model.DataOwner是传递给注解的owner值。

So this's where i'm stuck right now, any help is appreciated.所以这就是我现在陷入困境的地方,感谢任何帮助。 Thank you!谢谢!

Let's start with the reason the second approach doesn't work:让我们从第二种方法不起作用的原因开始:

The annotation returned by this method could contain an element whose value is of type Class. 此方法返回的注释可能包含一个元素,其值为 Class 类型。 This value cannot be returned directly: information necessary to locate and load a class (such as the class loader to use) is not available, and the class might not be loadable at all. 无法直接返回此值:定位和加载 class(例如要使用的 class 加载程序)所需的信息不可用,并且 class 可能根本无法加载。 Attempting to read a Class object by invoking the relevant method on the returned annotation will result in a MirroredTypeException, from which the corresponding TypeMirror may be extracted. 尝试通过在返回的注释上调用相关方法来读取 Class object 将导致 MirroredTypeException,从中可以提取相应的 TypeMirror。 Similarly, attempting to read a Class[]-valued element will result in a MirroredTypesException. 同样,尝试读取 Class[]-valued 元素将导致 MirroredTypesException。

which obviously applies to KClass as well.这显然也适用于KClass

So for classes in annotations you can only get a TypeMirror (implemented in this case by Type.ClassType , but that's an internal detail you shouldn't rely on) and not a KClass .因此,对于注释中的类,您只能获得TypeMirror (在这种情况下由Type.ClassType实现,但这是您不应该依赖的内部细节)而不是KClass Either by the first approach or通过第一种方法或

inline fun <reified T : Annotation> Element.getAnnotationClassValue(f: T.() -> KClass<*>) = try { 
    getAnnotation(T::class.java).f() 
    throw Exception("Expected to get a MirroredTypeException")
} catch (e: MirroredTypeException) {
    e.typeMirror
}

which can be used as可以用作

element.getAnnotationClassValue<Model> { owner }

and returns the TypeMirror for DataOwner .并返回TypeMirrorDataOwner

If you are using KSP then the solution can look something like this:如果您使用的是 KSP,则解决方案可能如下所示:

Using the experimental APIs and assuming the KClass you reference in the annotation is available:使用实验性 API 并假设您在注释中引用的 KClass 可用:

resolver.getSymbolsWithAnnotation("com.example.Model")
    .map {
        val consumerType = try {
            it.getAnnotationsByType(Model::class).single().consumer
            null
        } catch (e: KSTypeNotPresentException) {
            e.ksType
        }
        val consumerDeclaration = consumerType!!.declaration as KSClassDeclaration // etc
    }

If you can't do this, then maintaining a KSP view of the annotation is more flexible:如果你不能这样做,那么维护注释的 KSP 视图会更灵活:

resolver.getSymbolsWithAnnotation("com.example.Model")
    .map { ksAnnotated ->
        val args = ksAnnotated.annotations.single {
            it.shortName.asString() == "Model" && it.annotationType.resolve().declaration.qualifiedName?.asString() == "com.example.Model"
        }.arguments

        val consumerType = args.single { it.name?.asString() == "owner" }.value as KSType
        val consumerDeclaration = consumerType.declaration as KSClassDeclaration // etc 
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM