API设计中应优先用基本类型表达确定、不可空的值语义,仅在需三态语义或泛型时用包装类;引用类型用于建模实体与关系,封装相关字段为POJO以提升可读性、可维护性与可扩展性。

在 API 设计中,基本类型与引用类型的选用不是语法细节,而是影响接口清晰性、健壮性和长期可维护性的关键决策。选错类型,轻则引发隐式装箱/拆箱、空指针、参数误用,重则导致客户端行为不一致、序列化失败或难以演进。
优先使用基本类型表达确定、不可变的值语义
当字段代表一个明确、无状态、非空的量时(如用户 ID 的长度限制、重试次数、超时毫秒数),应直接使用 int、long、boolean 等基本类型。
- 避免用 Integer 表达“必须提供”的整数值——它引入了 null 的歧义,而 API 接口契约应明确“该值不可为空”
- 基本类型天然不可为 null,配合现代 Java(如 JDK 14+)的 @NonNull 注解或 Lombok 的 @RequiredArgsConstructor,能强化编译期校验
- 序列化(如 JSON)更稳定:
"timeout": 3000比"timeout": null或缺失字段更易被客户端解析和验证
谨慎使用包装类,仅当需要“三态语义”或参与泛型时
只有在真正需要表达“未设置 / 无效 / 缺失”三种状态之一时,才考虑 Integer、Boolean 等包装类型。
- 例如分页参数:Integer pageSize 可表示“使用默认值”,而 int pageSize 强制客户端传入数字,无法区分“显式设为 10”和“希望用系统默认”
- 注意:所有包装类型在反序列化失败时可能为 null,API 实现层必须主动判空并给出明确错误(如
400 Bad Request: 'limit' must be a valid integer),不能依赖 NPE 报错 - 避免在 DTO 中混用 int 和 Integer 表达同一业务概念(如
age有时是 int,有时是 Integer),这会破坏契约一致性
引用类型用于建模实体、关系与可扩展结构
当数据具有身份、生命周期、内部状态或需支持未来扩展(如新增字段、子类型)时,必须使用自定义引用类型(POJO/Record)。
- 不要把多个逻辑相关的字段拆成一堆独立的基本类型参数(如
String name, String email, String phone, int age);应封装为 UserInput 类——这使接口可读、可复用、可加校验注解(@NotBlank,@Email) - 引用类型天然支持 Optional 作为返回值(如
Optional<user> findUserById(long id)</user>),比返回 null 更明确地表达“可能不存在” - 面向演进:后续若需为用户增加
timezone或preferences字段,只需修改 UserInput 类,不破坏方法签名
避免基本类型与引用类型在边界处隐式转换
API 的输入/输出边界(如 Controller 层、RPC 入参、JSON 序列化)是装箱/拆箱风险高发区,必须显式管控。
- Spring MVC 中,不要依赖框架自动将 JSON 的
"id": null绑定到 Long id 参数(会报 400);应统一接收 Long 并检查是否为 null,或改用 Long 包装类 + 自定义 @Valid 校验器 - 禁止在 public API 方法签名中使用 new Integer(123) 或 Integer.valueOf("123") 等手动装箱——交给 JVM 的缓存机制(-128~127)或自动装箱即可
- 对外暴露的 SDK 或 OpenAPI Spec(Swagger)中,明确定义每个字段是
integer还是integer|null,避免客户端因语言差异(如 JS 的 number 无 null 概念)产生误解

















