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

成员列表变量空值校验:@NotNull列表与普通列表差异化处理

最优实现方案:基于反射+注解的通用列表校验/清理工具

针对你给出的类结构和校验需求,我推荐封装一个通用反射工具类来处理——这种方案既能覆盖所有嵌套层级的字段,又不用为每个类重复写逻辑,是最灵活高效的实现方式。


核心逻辑梳理

  1. 用反射遍历目标对象的所有字段,精准定位List类型的字段
  2. 针对列表字段分两种情况处理:
    • 带@NotNull注解的列表:严格检查元素,只要有null就抛出校验异常
    • 不带注解的列表:自动过滤掉所有null元素,保留有效数据
  3. 递归处理嵌套对象(比如A里的C、B,B里的E),确保所有层级的列表都被处理到

代码实现示例

首先定义一个自定义校验异常,用于抛出列表包含null的错误:

public class NotNullListContainsNullException extends RuntimeException {
    public NotNullListContainsNullException(String message) {
        super(message);
    }
}

然后是核心的工具类,实现反射解析和列表处理逻辑:

import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public class ListFieldProcessor {

    // 对外暴露的入口方法,传入需要处理的对象即可
    public static void process(Object target) throws IllegalAccessException {
        if (target == null) {
            return;
        }
        Class<?> clazz = target.getClass();
        // 遍历当前类的所有字段(如果需要处理父类字段,可以扩展逻辑)
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            Object fieldValue = field.get(target);
            
            // 处理List类型的字段
            if (List.class.isAssignableFrom(field.getType())) {
                List<?> list = (List<?>) fieldValue;
                if (list == null) {
                    continue; // 列表本身为null的场景,需求未明确要求,暂时跳过
                }
                
                // 检查字段是否标注了@NotNull注解
                if (field.isAnnotationPresent(NotNull.class)) {
                    // 校验列表中是否存在null元素
                    if (list.contains(null)) {
                        throw new NotNullListContainsNullException(
                            String.format("字段[%s]标注了@NotNull,但列表中包含null元素", field.getName())
                        );
                    }
                } else {
                    // 过滤掉列表中的null元素,替换原列表
                    List<Object> filteredList = new ArrayList<>();
                    for (Object item : list) {
                        if (item != null) {
                            filteredList.add(item);
                        }
                    }
                    field.set(target, filteredList);
                }
            }
            
            // 递归处理嵌套的自定义对象(排除基本类型、包装类和字符串)
            else if (!field.getType().isPrimitive() && !isWrapperType(field.getType()) && !String.class.equals(field.getType())) {
                process(fieldValue);
            }
        }
    }

    // 辅助方法:判断是否是基本类型的包装类
    private static boolean isWrapperType(Class<?> clazz) {
        return clazz.equals(Integer.class) || clazz.equals(Long.class) || 
               clazz.equals(Double.class) || clazz.equals(Float.class) || 
               clazz.equals(Boolean.class) || clazz.equals(Character.class) ||
               clazz.equals(Byte.class) || clazz.equals(Short.class);
    }
}

使用示例

public class Main {
    public static void main(String[] args) throws IllegalAccessException {
        // 构造测试对象
        A a = new A();
        a.ds = List.of("a", null, "b"); // 标注@NotNull的列表,包含null元素
        a.bs = List.of(new B());
        a.c = new C();
        a.c.s = List.of("x", null, "y"); // 未标注@NotNull的列表
        
        try {
            ListFieldProcessor.process(a);
        } catch (NotNullListContainsNullException e) {
            System.err.println(e.getMessage()); // 会输出:字段[ds]标注了@NotNull,但列表中包含null元素
        }
        
        // 处理后,a.c.s会被清理为["x", "y"]
        System.out.println(a.c.s);
    }
}

方案优势

  1. 通用性:不需要为每个实体类单独编写处理逻辑,传入任意对象即可完成全量字段处理
  2. 深层覆盖:自动递归处理嵌套对象中的列表字段,不会遗漏层级
  3. 可扩展性:可以轻松扩展支持Set等其他集合类型,或者添加更多注解规则
  4. 性能可控:反射开销在大多数业务场景下可忽略,若有性能需求可以缓存字段元信息

注意事项

  • 如果你的@NotNull不是javax.validation.constraints.NotNull,需要修改工具类中的注解引用
  • 对于不可变列表(比如List.of()创建的),工具类会自动创建新列表赋值,避免修改异常
  • 若需要处理父类的字段,可以扩展逻辑遍历所有父类的字段

内容的提问来源于stack exchange,提问作者Aditya Narayan Dixit

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:42:43