• 如何判断一个 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 才会相等。

     第一种情况

    type 和 data 都相等

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

    本篇原属于 Go 面试题库专栏系列文章,以前都是在标题上写明了是面试题,考虑到有些人最近没有在面试,怕你们错过这类即使不面试,也要掌握的知识,以后的内容,可能不会在标题上特别标明是面试题了。

    本专栏系列文章,我都公开到 Github 上:https://github.com/iswbm/golang-interview

    这个号没有留言功能呢 ,如果文章有写得不对的地方,可以去那里提交 issue 帮我指正。顺便可以帮我点个小 ⭐⭐,在那里我对题库进行了分类整理,方便索引查找。

    加油噢,我们下篇见

  • 相关阅读:
    每日英语:Italian town fighting for its life over polluting Ilva steelworks
    SpringBoot + Kafka + ELK 完成海量日志收集(超详细)
    如何使用 Java 生成二维码?
    xyntservice
    windows service 与GUI窗口的实现
    一个可以查看HTML网页上密码框的程序(附源码)
    vss6 forgot admin password
    vs2005's addin folder
    javascript tips
    html中使用imagemap
  • 原文地址:https://www.cnblogs.com/cheyunhua/p/15761198.html
Copyright © 2020-2023  润新知