Go中的零值与空值,你搞懂了么?

内置类型的零值/默认值

变量声明但未显式初始化时,Go 自动赋的默认值。它保证"未初始化"也有确定、可用的值

TYPE ZERO VALUE
Integer 0
Floating point 0.0
Boolean false
String ""
Pointer nil
Interface nil
Slice nil
Map nil
Array fields zero (每个元素都是其零值)
Struct fields zero (每个元素都是其零值)
Channel nil
Function nil

Json序列化中的omitempty&omitzero

omitempty

当对一个结构体的某个字段的tag设置为omitempty时,如果这个字段是空值(empty value,和零值不一样)时,那么这个字段就会在序列化时省略

The "omitempty" option specifies that the field should be omitted from the encoding if the field has an empty value, defined as false, 0, a nil pointer, a nil interface value, and any empty array, slice, map, or string.

omitempty标签的判断逻辑如下:

  • 对于布尔类型:false被视为空
  • 对于数值类型:0被视为空
  • 对于指针、接口:nil被视为空
  • 对于字符串:""(空字符串)被视为空
  • 对于数组、切片、map:长度为0被视为空,既包含未初始化的又包含空的

对于空结构体、所有字段都为默认值的零值结构体、以及一些实际含义为零的结构体(例如time.Time{}),omitempty都无能为力,会进行输出。

因此在Go 1.24之前想对某个结构体不输出,可选的一种方案是使用指针类型

go 复制代码
type VanillaStruct struct {
    StructTyp1 struct{}        // 空结构体
    StructTyp2 struct{ A int } // 零值结构体
    TimeTyp    time.Time       // 实际含义为零的结构体
​
    StructTyp3 *struct{}
    StructTyp4 *struct{ A int }
    TimeTyp2   *time.Time
}
​
type OmitemptyStruct struct {
    StructTyp1 struct{}        `json:",omitempty"`
    StructTyp2 struct{ A int } `json:",omitempty"`
    TimeTyp1   time.Time       `json:",omitempty"`
​
    StructTyp3 *struct{}        `json:",omitempty"`
    StructTyp4 *struct{ A int } `json:",omitempty"`
    TimeTyp2   *time.Time       `json:",omitempty"`
}
​
func main() {
    s1 := VanillaStruct{}
    ss1, err := json.Marshal(s1)
    fmt.Println(err, string(ss1)) 
    // {"StructTyp1":{},"StructTyp2":{"A":0},"TimeTyp":"0001-01-01T00:00:00Z","StructTyp3":null,"StructTyp4":null,"TimeTyp2":null}
​
    s2 := OmitemptyStruct{}
    ss2, err := json.Marshal(s2)
    fmt.Println(err, string(ss2)) 
    // {"StructTyp1":{},"StructTyp2":{"A":0},"TimeTyp1":"0001-01-01T00:00:00Z"}
}

omitzero

Go 1.24 给 encoding/json 新增omitzero:按零值(zero value)省略

在序列化时,omitzero选项指定如果字段值为零,则该结构体字段应被省略

  • 如果该类型定义了IsZero() bool方法,那么这个零值就通过IsZero方法来判断
  • 否则是根据字段是否是零值(通过reflect.Value.IsZero判断)来判断
go 复制代码
type VanillaStruct struct {
    StructTyp1 struct{}        // 空结构体
    StructTyp2 struct{ A int } // 零值结构体
    TimeTyp    time.Time       // 实际含义为零的结构体
}
​
type OmitzeroStruct struct {
    StructTyp1 struct{}        `json:",omitzero"`
    StructTyp2 struct{ A int } `json:",omitzero"`
    TimeTyp1   time.Time       `json:",omitzero"`
}
​
func main() {
    s1 := VanillaStruct{}
    ss1, err := json.Marshal(s1)
    fmt.Println(err, string(ss1))
    // {"StructTyp1":{},"StructTyp2":{"A":0},"TimeTyp":"0001-01-01T00:00:00Z"}
​
    s2 := OmitzeroStruct{}
    ss2, err := json.Marshal(s2)
    fmt.Println(err, string(ss2))
    // {}
}

总结

下面是一个完整的Go示例,展示了omitempty&omitzero标签在不同类型上的应用:

TYPE ZERO VALUE EMPTY VALUE WITHOUT OMIT OMITEZERO OMITEMPTY
Integer 0 0 0 OMIT OMIT
Floating point 0 0 0 OMIT OMIT
Boolean FALSE FALSE FALSE OMIT OMIT
String "" "" "" OMIT OMIT
Pointer nil nil null OMIT OMIT
Interface nil nil null OMIT OMIT
Slice nil nil null OMIT OMIT
Slice nil make([]T, 0) [] [] OMIT
Map nil nil null OMIT OMIT
Map nil make(map[K]V, 0) {} {} OMIT
Array: [0]T fields zero [0]T [] OMIT OMIT
Array: [N]T fields zero [T] OMIT [T]
Struct: struct{} fields zero {} OMIT {}
Struct: struct{ F T } fields zero {"F": T} OMIT {"F": T}
Channel nil nil
Function nil nil

✨ 微信公众号【凉凉的知识库】同步更新,欢迎关注获取最新最有用的知识 ✨

相关推荐
JAVA面经实录9175 小时前
Java企业级工程化·终极完整版背诵手册(无遗漏、全覆盖、面试+落地通用)
java·开发语言·面试
小程故事多_807 小时前
[大模型面试系列] 多轮对话 Agent 设计实战(含窗口优化 + 工具调用精髓)
人工智能·面试·职场和发展
AI人工智能+电脑小能手12 小时前
【大白话说Java面试题】【Java基础篇】第24题:Java面向对象有哪些特征
java·开发语言·后端·面试
迦南的迦 亚索的索12 小时前
AI_11_Coze_AI面试助手
人工智能·面试·职场和发展
AI人工智能+电脑小能手12 小时前
【大白话说Java面试题】【Java基础篇】第25题:JDK1.8的新特性有哪些
java·开发语言·后端·面试
KmSH8umpK13 小时前
Redis分布式锁从原生手写到Redisson高阶落地,附线上死锁复盘优化方案进阶第三篇
redis·分布式·wpf
逻辑驱动的ken14 小时前
Java高频面试场景题19
java·开发语言·面试·职场和发展·求职招聘
KmSH8umpK16 小时前
SpringBoot 分布式锁实战:从单机锁到Redis分布式锁全覆盖,解决超卖、重复下单、幂等并发问题
spring boot·redis·分布式
笨鸟先飞的橘猫17 小时前
通用后端面试题收集(持续中)
面试
KmSH8umpK19 小时前
Redis分布式锁从原生手写到Redisson高阶落地,附线上死锁复盘优化方案
redis·分布式·wpf