Can I get a class's name as a compile-time constant without hardcoding it in a string literal?

喜欢而已 提交于 2019-12-06 17:38:38

问题


I am working on an annotation processor. This code compiles:

package sand;

import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.TypeElement;

@SupportedAnnotationTypes("sand.Foo")
public class FooProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        return false; // TODO
    }
}

However, I am displeased by the string constant "sand.Foo" (not too much in this case, but more in general for the future).

If Foo is renamed or moved to another package, this code will still compile, but it won't work.

I would like to do something like:

@SupportedAnnotationTypes(Foo.class)

That way, if Foo's name changes, the compilation will fail and someone will have to correct the file.

But this does not work because a Class is not a String. So I tried:

@SupportedAnnotationTypes(Foo.class.getName())

But the compiler does not consider this a constant expression, which is required in this context, so that won't work either.

Is there any way to coerce a class literal into its name at compile time?


回答1:


Instead of using the annotation, your processor can implement getSupportedAnnotationTypes() to provide supported annotation type names at runtime:

Set<String> getSupportedAnnotationTypes() {
    Set<String> supportedAnnotationTypes = new HashSet<>();
    supportedAnnotationTypes.add(Foo.class.getName());
    return supportedAnnotationTypes;
} 



In case you'd like to keep using (non-standard) annotations for this, you could create your own annotation that takes a compile time type as argument, like @k_g suggested. @SupportedAnnotationTypes isn't really anything special, it is only used automatically when you are extending AbstractProcessor anyway. Take a look at the source code of AbstractProcessor.getSupportedAnnotationTypes().

The signature of your custom annotation should use Class<?>[] instead of String[]:

@Target(TYPE)
@Retention(RUNTIME)
public @interface SupportedAnnotationTypes {
    Class<?>[] value();
}

Override getSupportedAnnotationTypes and look up your custom annotation in the same way as AbstractProcessor. For example like this:

public Set<String> getSupportedAnnotationTypes() {
    Class<?>[] types = getClass().getAnnotation(SupportedAnnotationTypes.class).value();
    return Arrays.stream(types).map(Class::getName).collect(Collectors.toSet());
}



回答2:


You can define your own.

public @interface SupportedAnnotationTypes_Class {
    Class supported();
}

and then use @SupportedAnnotationTypes_Class(supported = sand.Foo.class) to use it.



来源:https://stackoverflow.com/questions/27955169/can-i-get-a-classs-name-as-a-compile-time-constant-without-hardcoding-it-in-a-s

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!