You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建枚举新增值的编译时校验规则?限制sequential后不可加parallel

编译阶段约束枚举新增值的校验方案

核心需求

当枚举中已存在带"sequential"参数的枚举值后,禁止添加带"parallel"参数的新枚举值。例如现有枚举:

public enum Execution {
    TAKS_N("parallel"),
    TASK_A("sequential"),
    TASK_D("sequential")
}

允许在sequential值之前添加新的parallel值,但禁止在sequential之后添加parallel值。

编译阶段实现方案

单元测试属于运行时校验,若要在编译阶段直接拦截违规操作,可使用Java注解处理器(Annotation Processor)——这是Java官方提供的编译期扩展机制,能在编译时扫描、处理注解,对代码结构做校验,不符合规则直接抛出编译错误。

步骤1:自定义校验注解

先定义一个注解,用来标记需要校验的枚举:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface SequentialFirstConstraint {
    // 指定枚举构造方法中用来区分类型的参数索引(这里是第一个参数,默认值为0)
    int typeParamIndex() default 0;
    // 触发禁止规则后不允许新增的参数值
    String forbiddenValue() default "parallel";
    // 用来触发禁止规则的参数值
    String triggerValue() default "sequential";
}

步骤2:实现注解处理器

编写注解处理器逻辑,在编译时扫描标记了@SequentialFirstConstraint的枚举,检查枚举常量的顺序和参数:

@SupportedAnnotationTypes("com.yourpackage.SequentialFirstConstraint")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class SequentialConstraintProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (TypeElement annotation : annotations) {
            Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(annotation);
            for (Element element : elements) {
                if (element.getKind() == ElementKind.ENUM) {
                    checkEnumConstraint((TypeElement) element);
                }
            }
        }
        return true;
    }

    private void checkEnumConstraint(TypeElement enumElement) {
        SequentialFirstConstraint constraint = enumElement.getAnnotation(SequentialFirstConstraint.class);
        int paramIndex = constraint.typeParamIndex();
        String forbiddenValue = constraint.forbiddenValue();
        String triggerValue = constraint.triggerValue();

        boolean triggerFound = false;

        // 遍历所有枚举常量
        for (Element enumConstant : enumElement.getEnclosedElements()) {
            if (enumConstant.getKind() == ElementKind.ENUM_CONSTANT) {
                // 获取枚举常量的构造方法参数
                ExecutableElement constructor = (ExecutableElement) enumConstant.getEnclosedElements().stream()
                        .filter(e -> e.getKind() == ElementKind.CONSTRUCTOR)
                        .findFirst()
                        .orElseThrow(() -> new IllegalStateException("Enum constant has no constructor"));

                // 获取指定索引的参数值(假设为字符串常量)
                if (constructor.getParameters().size() > paramIndex) {
                    Expression valueExpr = ((VariableElement) constructor.getParameters().get(paramIndex)).getInitializer();
                    if (valueExpr instanceof StringLiteralExpr) {
                        String paramValue = ((StringLiteralExpr) valueExpr).getValue();

                        if (triggerValue.equals(paramValue)) {
                            triggerFound = true;
                        } else if (triggerFound && forbiddenValue.equals(paramValue)) {
                            // 抛出编译错误
                            processingEnv.getMessager().printMessage(
                                    Diagnostic.Kind.ERROR,
                                    "禁止在已存在'" + triggerValue + "'类型的枚举值后添加'" + forbiddenValue + "'类型的新值",
                                    enumConstant
                            );
                        }
                    }
                }
            }
        }
    }
}

步骤3:注册注解处理器

在项目的META-INF/services/javax.annotation.processing.Processor文件中添加处理器的全类名:

com.yourpackage.SequentialConstraintProcessor

如果用Maven,也可以通过maven-compiler-plugin配置,或者引入com.google.auto.service:auto-service依赖,用@AutoService注解简化注册流程。

步骤4:在目标枚举上应用注解

将注解加到需要校验的枚举类上:

@SequentialFirstConstraint
public enum Execution {
    TAKS_N("parallel"),
    TASK_ALLOWED("parallel"), // 允许,此时还未出现sequential类型
    TASK_A("sequential"),
    TASK_D("sequential"),
    TASK_NOT_ALLOWED("parallel") // 编译时直接报错!
}

替代方案:自定义Lint规则

如果是Android项目,也可以自定义Lint规则,逻辑和注解处理器类似,但Lint更偏向代码规范检查,同样能在编译阶段抛出错误。不过注解处理器是通用Java项目都支持的方案。

方案对比

  • 单元测试:仅在运行时校验,存在漏检风险(比如未执行测试就提交代码)。
  • 注解处理器:编译阶段直接拦截,强制符合规则,从源头避免违规代码流入代码库。

内容的提问来源于stack exchange,提问作者Moritz Kampfinger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 22:25:23