如何判断一个 interface{} 的值是否为 nil ?

引言

在开始之前,先考你一个非常 Go 味的经典问题:如何判断一个 interface{} 的值是否为 nil ?

这也是面试有可能会被问到的一个问题,这个问题很 “迷”,平时没有特别留心的朋友,很容易在这边裁了。

我相信很多人会下意识的回答,直接 v == nil 进行判断不就好了吗?

很久之前,我也是那么想的,可写了个 demo 后,才发现事情没那么简单。

请看下面这段代码,可以先猜测一下输出的结果。

package main

import (
    "fmt"
)

func main()  {
    var a *string = nil
    var b interface{} = a

    fmt.Println(b==nil) 
}

答案应该会跟你下意识的回答 相反。

输出的结果的是 false

这是为什么呢?接下来,我们就要好好的唠唠这里面的道道。

1. 两个 interface 比较

interface 的内部实现包含了两个字段,一个是 type,一个是 data

对于这样一个变量

var age interface{} = 25

其实内部结构是
在这里插入图片描述

因此两个 interface 比较,势必与这两个字段有所关系。

经过验证,只有下面两种情况,两个 interface 才会相等。

第一种情况

typedata 都相等

在下面的代码中,p1 和 p2 的 type 都是 Profile,data 都是 {"iswbm"},因此 p1 与 p2 相等

而 p3 和 p3 虽然类型都是 *Profile,但由于 data 存储的是结构体的地址,而两个地址和不相同,因此 p3 与 p4 不相等

package main

import "fmt"

type Profile struct {
    Name string
}

type ProfileInt interface {}

func main()  {
    var p1, p2 ProfileInt = Profile{"iswbm"}, Profile{"iswbm"}
    var p3, p4 ProfileInt = &Profile{"iswbm"}, &Profile{"iswbm"}

    fmt.Printf("p1 --> type: %T, data: %v \n", p1, p1)
    fmt.Printf("p2 --> type: %T, data: %v \n", p2, p2)
    fmt.Println(p1 == p2)  // true

    fmt.Printf("p3 --> type: %T, data: %p \n", p3, p3)
    fmt.Printf("p4 --> type: %T, data: %p \n", p4, p4)
    fmt.Println(p3 == p4)  // false
}

运行后,输出如下

p1 --> type: main.Profile, data: {iswbm} 
p2 --> type: main.Profile, data: {iswbm} 
true
p3 --> type: *main.Profile, data: 0xc00008e200 
p4 --> type: *main.Profile, data: 0xc00008e210 
false

第二种情况

特殊情况:两个 interface 都是 nil

当一个 interface 的 type 和 data 都处于 unset 状态的时候,那么该 interface 的类型和值都为 nil

package main

import "fmt"

func main() {
    var p1, p2 interface{}
    fmt.Println(p1 == p2)  // true
    fmt.Println(p1 == nil) // true
}

2. interface 与 非 interface 比较

当 interface 与非 interface 比较时,会将 非interface 转换成 interface ,然后再按照 两个 interface 比较 的规则进行比较。

示例如下

package main

import (
    "fmt"
    "reflect"
)

func main()  {
    var a string = "iswbm"
    var b interface{} = "iswbm"
    fmt.Println(a==b) // true
}

上面这种例子可能还好理解,那么请你看下面这个例子(文章开头的示例),为什么 b 与 nil 不相等?

package main

import (
    "fmt"
)

func main()  {
    var a *string = nil
    var b interface{} = a

    fmt.Println(b==nil) // false
}

但当你使用 b==nil 进行判断时,其实右边的 nil 并非单纯的是我们所理解的值为nil,而正确的理解应该是 类型为 nil 且 值也为 nil。

Go 会先将 nil 转换为interface (type=nil, data=nil) ,这与 b (type=*string, data=nil) 虽然 data 是一样的,但 type 不相等,因此他们并不相等。

那有没有办法判断一个 interface{} 是不是 nil 呢?

有办法的,但是要借助反射,一个非万不得已不会使用的 reflect 包。

package main

import (
    "fmt"
    "reflect"
)

func IsNil(i interface{}) bool {
    vi := reflect.ValueOf(i)
    if vi.Kind() == reflect.Ptr {
        return vi.IsNil()
    }
    return false
}

func main() {
    var a *string = nil
    var b interface{} = a
    fmt.Println(IsNil(b))
}

本文通过一些例子介绍了 Go 在比较时候,内部的一些实现原理,对于很多人,可能是一个“新”知识,没有掌握的话,一定会在以后在编码过程中给自己挖了个“大坑”,而这种语言内部的 “坑”,不知道就是不知道,再怎么代码走查都很难发现。希望通过本篇文章,带你一起把这个“坑” 给填上。

更多功能请持续关注!!!!!

欢迎各位加我的微信(jobhandsome)跟我一起完成并推动项目的发展在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码一行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值