Go 零值和空值的判断问题

大家好,我是煎鱼。

前段时间分享了《Go 将增加内置的零值标识符 zero!》的新预定义标识符 zero

对应的签名如下:

// zero is a predeclared identifier representing the zero value
// for array and struct types.
var zero Type

我原想着还是一个有一点点新改变。不过综合大家意见来看,由于只是针对数组(array)和结构体(struct),许多同学表示这个是比较鸡肋的。因为仍然无法很好的解决 Go 零值和空值的识别问题,大失所望。

本文是对零值和空值判断现状进行梳理和分享。

快速复习零值

基本类型

var a int
var b bool
var c string

func main() {
 fmt.Printf("%+v\n", a) // 0
 fmt.Printf("%+v\n", b) // false
 fmt.Printf("%+v\n", c) // ""
}

复合类型

var a []int
var b map[string]int
var c [7]int
var d *int
var g chan int
var p Person

type Person struct {
 Name string
 Age  int
}

func main() {
 fmt.Printf("%+v\n", a) // []
 fmt.Printf("%+v\n", b) // map[]
 fmt.Printf("%+v\n", c) // [0 0 0 0 0 0 0]
 fmt.Printf("%+v\n", d) // <nil>
 fmt.Printf("%+v\n", g) // <nil>
 fmt.Printf("%+v\n", p) // {Name: Age:0}
}

进行空值判断

在实际的 Go 业务应用中,我们需要对数据的零值和空值进行区分,以便于实现一些空值的业务逻辑处理。(初入门的同学经常在此踩坑)

常见的有两种做法。如下:

  1. 在变量声明时,使用指针来处理,将其声明为指针类型。

  2. 在定义变量缺省值时,错开类型的零值。例如:int 零值是 0,业务里字段缺省值定义为 1 和 2 等。

第一种是用的最多的,也是前文评论区大家有所提到的。对于基础类型,具体的代码示例如下:

var a *int
var b *bool
var c *string

func main() {
 if a == nil {
  fmt.Print("煎鱼")
 }
 if b == nil {
  fmt.Print("进")
 }
 if c == nil {
  fmt.Print("脑子了")
 }
}

输出结果:煎鱼进脑子了。

对于复合类型,也是一样的:

var a []int
var b map[string]int
var g chan int

var c *[7]int
var d *int
var p *Person

对于复合类型的一些值类型,由于零值有可能是程序赋的值,也有可能是真空值。因此同样需要加上指针,用于识别空值和零值。

在 Go 业务程序上,大家为了解决这个零值和空值的判别问题。会采取上述类似的方式去编写包和程序。

如下演示代码:

type Person struct {
 Name *string
 Age  *int
}

func main() {
 s := `{
  "name": "煎鱼"
 }`

 var p Person
 err := json.Unmarshal([]byte(s), &p)
 if err != nil {
  fmt.Println(err)
 }
 fmt.Printf("p: %+v\n", p)
 fmt.Println(*p.Name)
}

输出结果:

p: {Name:0xc00010c380 Age:<nil>}
煎鱼

可以看到所传入的 json 字符串并不包含 age 字段,因此其值为 nil。

如果是为空字符串:

s := `{
  "name": ""
 }`

解析后输出的结果为:

p: {Name:0xc000096380 Age:<nil>}

以此就可以实现空值和零值的有效区分,不再为此判别烦恼太多。

但也引入了一个麻烦的点,就是在获取值时需要使用 *p.Name 的方式。如果希望 “屏蔽” 这个用法,一般还会再做一次函数封装作为 Getter 的方法。

新增 zero 解决什么问题

显然我再回去看即将新加入的 zero 标识符时,会发现他能够成功 Go 的机缘是对零值的比较判断,而并非空值的原因。

zero 使用场景是:

if val == zero(MyType) {}

又或是:

func example[T any]() T {
  // do something that returns an error...
  if err != nil {
    return zero(T)
  }
  //...
}

这么一梳理,发现确实和我们想象中的有一定的差距。因为我们在实际的零值和空值的判断中,更需要的是对内部字段的数值判断例如:结构体里的某些字段,比较少是只对结构体本身做空值判断。

总结

今天根据大家热议的反馈,重新梳理了 Go 中零值和空值的现状和判断技巧。感觉本次 zero 的加入,真的是只加强了结构体和数组类型本身的零值判断,而没有针对空值的好手段。

综合来看,考虑到规范(SPEC)中零值是官方的规范约定,改变的可能性也很低了。个人感觉核心团队新增判别方式或优化的可能性比较低。

大家平时除了使用指针和预定义非零值的枚举值外,还会用什么方法来判别零值和空值呢,也欢迎大家分享你的看法!

推荐阅读

关注和加煎鱼微信,

一手消息和知识,拉你进技术交流群👇

27d0757ccc540439da2ca255a7a01cb2.jpeg

ca5e6aede6b93dbc7af9755a12981923.png

你好,我是煎鱼,出版过 Go 畅销书《Go 语言编程之旅》,再到获得 GOP(Go 领域最有观点专家)荣誉,点击蓝字查看我的出书之路

日常分享高质量文章,输出 Go 面试、工作经验、架构设计,加微信拉读者交流群,和大家交流!

在Java中,判断空值和null值是有区别的。空值是指一个对象没有被实例化,而null值是指一个对象的引用指向了空地址。在判断空值时,可以使用isEmpty()方法来判断字符串是否为空,或者使用length()方法来判断数组是否为空。而在判断null值时,可以使用==运算符来判断一个对象是否为null。此外,还可以使用Optional类来处理可能为空的对象。Optional类是Java 8引入的一个容器类,它可以包装一个对象,如果对象为空,则返回一个空的Optional对象,如果对象非空,则返回一个包含该对象的Optional对象。可以使用Optional的isPresent()方法来判断Optional对象是否为空,或者使用orElse()方法来获取Optional对象中的值,如果为空则返回一个默认值。总之,在Java中判断空值和null值的方法有很多种,可以根据具体的需求选择合适的方法来判断。\[2\] #### 引用[.reference_title] - *1* [null或空值判断处理-java](https://blog.csdn.net/Ghost_T/article/details/5811485)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Java如何优雅的进行判空](https://blog.csdn.net/ww2651071028/article/details/130335753)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值