我可以在不将 class 的名称硬编码为字符串文字的情况下将其作为编译时常量吗?

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

我正在研究注释处理器。此代码编译:

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

但是,我对字符串常量 "sand.Foo" 感到不满意(在这种情况下不是太多,但将来会更普遍)。

如果 Foo 被重命名或移动到另一个包,此代码仍可编译,但无法运行。

我想做这样的事情:

@SupportedAnnotationTypes(Foo.class)

那样的话,如果 Foo 的名字改变了,编译就会失败,就得有人来更正文件了。

但这不起作用,因为 Class 不是 String。所以我尝试了:

@SupportedAnnotationTypes(Foo.class.getName())

但是编译器不认为这是一个常量表达式,而这在这种情况下是必需的,所以这也行不通。

有什么方法可以在编译时将 class 文字强制转换为它的名称吗?

您的处理器可以实现 getSupportedAnnotationTypes() 以在运行时提供支持的注释类型名称,而不是使用注释:

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



如果您想为此继续使用(非标准)注解,您可以创建自己的注解,将编译时类型作为参数,如@k_g 所建议的那样。 @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().

您的自定义注释的签名应使用 Class<?>[] 而不是 String[]

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

覆盖 getSupportedAnnotationTypes 并以与 AbstractProcessor 相同的方式查找您的自定义注释。例如像这样:

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

您可以定义自己的。

public @interface SupportedAnnotationTypes_Class {
    Class supported();
}

然后用@SupportedAnnotationTypes_Class(supported = sand.Foo.class)来使用。