英文:
Javax Annotation Processing: Check if an annotated type is a sub-type of another type
问题
我正在创建一个AnnotationProcessor,我需要检查被注释的类型是否是指定类的子类型。
这是注解:
public @interface Component {
Class<?> supertype();
}
示例:(正确)
@Component(supertype = MyInterface.class)
public class MyClass implements MyInterface {
// ...
}
示例:(错误,这不会编译,因为MyClass
不是String
的子类型)
@Component(supertype = String.class)
public class MyClass implements MyInterface {
}
我知道无法获取被注释的Class
,因为它尚未编译。
英文:
I'm creating an AnnotationProcessor and I need to check if the annotated type is a subtype of an specified class.
The annotation:
public @interface Component {
Class<?> supertype();
}
Example: (correct)
@Component(supertype = MyInterface.class)
public class MyClass implements MyInterface {
// ...
}
Example: (incorrect, this must not compile because MyClass
isn't a subtype of String
)
@Component(supertype = String.class)
public class MyClass implements MyInterface {
}
I know that I cannot get the annotated Class
because it isn't compiled yet.
答案1
得分: 0
你在问题中没有提到,但我猜想你遇到了TypeMirror
的问题。
下面是一个非常老旧且不太正规的解决方案,用于获取supertype
字段的TypeMirror
,并将其与类型的基类进行比较:
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
for (Element element : roundEnvironment.getElementsAnnotatedWith(Component.class)) {
// TODO: 在继续之前确保元素是一个类
TypeElement typeElement = (TypeElement) element;
Component componentAnnotation = typeElement.getAnnotation(Component.class);
TypeMirror superType = null;
try {
// 通过一些技巧快速获取注解属性的TypeMirror
componentAnnotation.supertype();
} catch (MirroredTypeException mte) {
superType = mte.getTypeMirror();
}
// TODO: 检查 superType 是否为 null
Set<TypeMirror> validTypes = Sets.newHashSet(typeElement.getInterfaces());
validTypes.add(typeElement.getSuperclass());
if (!validTypes.contains(superType)) {
// TODO: 抛出更好的异常信息
throw new IllegalArgumentException(typeElement.toString() + " 不实现或继承自 @Component 注解中声明的 "
+ superType.toString() + " 类型");
}
}
return true;
}
如果我是你,我会更深入地研究一下,看看是否有更好的解决方案。不过如果没有的话,这个方法可以解决你的问题。
英文:
You don't mention it in your question, but I assume you're running into TypeMirror
issues.
Here's a very old and hacky solution to get to the TypeMirror
of your supertype
field, and compare it against the type's base class:
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
for (Element element : roundEnvironment.getElementsAnnotatedWith(Component.class)) {
// TODO: Ensure element is a class before continuing
TypeElement typeElement = (TypeElement) element;
Component componentAnnotation = typeElement.getAnnotation(Component.class);
TypeMirror superType = null;
try {
// Hack to quickly get to TypeMirror of the annotation property
componentAnnotation.supertype();
} catch (MirroredTypeException mte) {
superType = mte.getTypeMirror();
}
// TODO: superType null check
Set<TypeMirror> validTypes = Sets.newHashSet(typeElement.getInterfaces());
validTypes.add(typeElement.getSuperclass());
if (!validTypes.contains(superType)) {
// TODO: throw something better
throw new IllegalArgumentException(typeElement.toString() + " does not implement or inherit from "
+ superType.toString() + " declared in @Component annotation");
}
}
return true;
}
If I were you I'd look into this a bit more to see if there are better options these days, but if not this will solve your problem.
答案2
得分: 0
这个版本不依赖于异常,它使用了 Element.getAnnotationMirrors
方法,这个方法可能是为了这种类型的用法而存在的。不过代码更冗长:
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
for (Element element : roundEnvironment.getElementsAnnotatedWith(Component.class)) {
// TODO: 在继续之前确保 element 是一个类
TypeMirror superType = null;
boolean found = false;
for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
TypeElement annotationElement = (TypeElement) mirror.getAnnotationType().asElement();
if (annotationElement.getQualifiedName().contentEquals(Component.class.getName())) {
found = true;
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> kv :
mirror.getElementValues().entrySet()) {
ExecutableElement member = kv.getKey();
if (member.getKind() == ElementKind.METHOD &&
member.getSimpleName().contentEquals("supertype")) {
superType = (TypeMirror) kv.getValue().getValue();
break;
}
}
}
if (found) {
break;
}
}
// TODO: 对 superType 进行空值检查
TypeElement typeElement = (TypeElement) element;
Set<TypeMirror> validTypes = Sets.newHashSet(typeElement.getInterfaces());
validTypes.add(typeElement.getSuperclass());
if (!validTypes.contains(superType)) {
// TODO: 抛出更合适的异常
throw new IllegalArgumentException(typeElement.toString() + " 不实现也不继承自 "
+ superType.toString() + ",这在 @Component 注解中声明过");
}
}
return true;
}
英文:
This version does not rely on exceptions, it uses Element.getAnnotationMirrors
which is probably there for this type of use. It's a lot more verbose though:
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
for (Element element : roundEnvironment.getElementsAnnotatedWith(Component.class)) {
// TODO: Ensure element is a class before continuing
TypeMirror superType = null;
boolean found = false;
for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
TypeElement annotationElement = (TypeElement) mirror.getAnnotationType().asElement();
if (annotationElement.getQualifiedName().contentEquals(Component.class.getName())) {
found = true;
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> kv :
mirror.getElementValues().entrySet()) {
ExecutableElement member = kv.getKey();
if (member.getKind() == ElementKind.METHOD &&
member.getSimpleName().contentEquals("supertype")) {
superType = (TypeMirror) kv.getValue().getValue();
break;
}
}
}
if (found) {
break;
}
}
// TODO: superType null check
TypeElement typeElement = (TypeElement) element;
Set<TypeMirror> validTypes = Sets.newHashSet(typeElement.getInterfaces());
validTypes.add(typeElement.getSuperclass());
if (!validTypes.contains(superType)) {
// TODO: throw something better
throw new IllegalArgumentException(typeElement.toString() + " does not implement or inherit from "
+ superType.toString() + " declared in @Component annotation");
}
}
return true;
}
答案3
得分: 0
使用以下代码获取注解中类值的 TypeMirror
public Optional<TypeMirror> getClassValueFromAnnotation(Element element, Class<? extends Annotation> annotation, String paramName) {
for (AnnotationMirror am : element.getAnnotationMirrors()) {
if (types.isSameType(am.getAnnotationType(), elements.getTypeElement(annotation.getCanonicalName()).asType())) {
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : am.getElementValues().entrySet()) {
if (paramName.equals(entry.getKey().getSimpleName().toString())) {
AnnotationValue annotationValue = entry.getValue();
return Optional.of((DeclaredType) annotationValue.getValue());
}
}
}
}
return Optional.empty();
}
然后使用这段代码检查它是否是特定类的超类型
/**
* 对 {@link Types#isAssignable(TypeMirror, TypeMirror)} 的包装,将在调用封装方法之前对 targetClass 进行类型擦除。
*
* @param typeMirror 一个 {@link javax.lang.model.type.TypeMirror} 对象。
* @param targetClass 一个 {@link java.lang.Class} 对象。
* @return 一个布尔值。
*/
public boolean isAssignableFrom(TypeMirror typeMirror, Class<?> targetClass) {
return types.isAssignable(typeMirror, types.erasure(elements.getTypeElement(targetClass.getCanonicalName()).asType()));
}
英文:
Use the following code to get the TypeMirror of the class value in the annotation
public Optional<TypeMirror> getClassValueFromAnnotation(Element element, Class<? extends Annotation> annotation, String paramName) {
for (AnnotationMirror am : element.getAnnotationMirrors()) {
if (types.isSameType(am.getAnnotationType(), elements.getTypeElement(annotation.getCanonicalName()).asType())) {
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : am.getElementValues().entrySet()) {
if (paramName.equals(entry.getKey().getSimpleName().toString())) {
AnnotationValue annotationValue = entry.getValue();
return Optional.of((DeclaredType) annotationValue.getValue());
}
}
}
}
return Optional.empty();
}
then use this code to check if it is a super type of a specific class
/**
* A wrapper over {@link Types#isAssignable(TypeMirror, TypeMirror)} which will apply type erasure on the targetClass before calling the wrapped method.
*
* @param typeMirror a {@link javax.lang.model.type.TypeMirror} object.
* @param targetClass a {@link java.lang.Class} object.
* @return a boolean.
*/
public boolean isAssignableFrom(TypeMirror typeMirror, Class<?> targetClass) {
return types.isAssignable(typeMirror, types.erasure(elements.getTypeElement(targetClass.getCanonicalName()).asType()));
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论