字段可空性
本页说明 Fory 在跨语言(xlang)序列化模式下如何处理字段可空性。
默认行为
在 xlang 模式下,字段默认都是不可空的。这意味着:
- 字段值必须始终存在,不能为 null
- 不会为该字段额外写入 null 标记字节
- 序列化结果更紧凑
以下类型默认是可空的:
- Java 和 C++ 可空包装类型:
Optional<T> - Java 装箱类型(
Integer、Long、Double等) - Go 指针类型(
*int32、*string等) - Rust
Option<T> - Python 类型提示:
Optional[T]
| 字段类型 | 默认可空 | 是否写入 null 标记 |
|---|---|---|
基础类型(int、bool、float 等) | 否 | 否 |
String | 否 | 否 |
List<T>、Map<K,V>、Set<T> | 否 | 否 |
| 自定义结构体 | 否 | 否 |
| 枚举 | 否 | 否 |
Java 装箱类型(Integer、Long 等) | 是 | 是 |
Go 指针类型(*int32、*string) | 是 | 是 |
Optional<T> / Option<T> | 是 | 是 |
编码格式
字段是否可空决定了值前面是否需要写入 null 标记字节:
不可空字段: [value data]
可空字段: [null_flag] [value data if not null]
其中 null_flag 的含义如下:
-1(NULL_FLAG):值为 null-2(NOT_NULL_VALUE_FLAG):值存在
可空性与引用跟踪
这两个概念相关,但并不相同:
| 概念 | 目的 | 标记值 |
|---|---|---|
| 可空性 | 允许字段值为 null | -1(null)、-2(非 null) |
| 引用跟踪 | 对共享引用做去重 | -1(null)、-2(非 null)、≥0(引用 ID) |
关键区别:
- 仅可空:只会写入
-1或-2,不会去重共享引用。 - 引用跟踪:在可空语义之上增加引用 ID(
≥0),用于表示已出现过的对象。 - 二者占用的是同一个标记字节位置,引用跟踪可以理解为可空机制的超集。
当 refTracking=true 时,这个标记字节会同时承担引用标记的职责:
ref_flag = -1 -> null 值
ref_flag = -2 -> 新对象(第一次出现)
ref_flag >= 0 -> 指向索引为 ref_flag 的已序列化对象
更详细的引用跟踪行为可参考 Reference Tracking。