056-接口值(Interface Value)

目前为止,我们已经学过很多类型的值了,比如 int 值,float 值,后面又学过函数值,方法值。到了接口这里也不例外,我们需要学习接口值(Interface value)。

之所以单独拿出来讲,是因为接口值也有它与众不同的地方。

1. 接口值(Interface Value)

概念上讲,接口值由两部分组成:

  • 具体类型 (Type)
  • 该类型对应的值 (Value)

之所以加一个修饰『从概念上讲』,是因为 go 内部具体实现不一定会严格按照这种方式来做,比如可能会扩充额外的字段。

这可能会让你一脸懵逼,看一张图举几个例子你就明白了。

一个被初始化为 0 值的接口,就像图 1 中那样,type 和 value 部分都是 nil.


这里写图片描述
图1 接口值构成

2. 示例

下面这个示例展示了一个空接口被赋值的情况:

var e interface{}
fmt.Println(e, e == nil) // Output: <nil> true

e = 5
fmt.Println(e, e == nil) // Output: 5 false

var x *int
e = x
fmt.Println(e, e == nil) // <nil> false

上面三种输出结果,我们依次分析。

  • 接口值为 nil

    一个接口值为 nil,需要接口值的两个组成部分都为 nil,即 type 为 nil,value 也为 nil。对于一个使用 var e interface{} 声明的空接口来说,这两个部分都被初始化为了 0 值,因此打印的时候,e 为 <nil>,且 e == nil 比较结果也为真。

  • e = 5

    此时为接口赋值为 5,e 的两个组成部分会变成下面这样:


这里写图片描述
图2 接口值

因此在打印的时候,e 输出的值为 5,而 e == nil 输出的值为 false.

  • e = x

这算是一种比较特殊的情况。x 是一个类型为 *int 的指针,x 的值为 nil。将 x 赋值给 e 后,e 的两个组成部分变成下面这样:


这里写图片描述
图3 接口值

最后打印的结果非常奇怪,e 输出了 <nil>e == nil 却输出了 false. 其实这非常容易解释,刚刚我们就说过了,只有接口值的两部分都为 nil,接口值才是 nil。

那为什么 e 输出的是 nil 呢?我们只能说,println 只能打印出 e 的 value 部分,而无法打印出 e 的 type 部分。如果打印 type 部分,你可以像下面这样做:

fmt.Printf("%v %T %v\n", e, e, e == nil) // Output: <nil> *int false

3. 接口值比较

接口值之间可以比较相等,接口值与普通值也可以进行比较。

接口值比较会有两步:

  • 比较 type 部分
  • 比较 value 部分

如果 type 不同,== 操作会返回 false. 如果 type 相等,再比较 value,如果 value 也相等,那就返回 true 了。

然而,坑点在于 value 部分。如果 value 本身是可以比较的,那么接口值就可以比较。如果 value 不可以比较,会报 panic 错误。

比如 slice 类型,只能和 nil 值进行比较,而 slice 之间是不可比较的。下面的比较会报错:

// 编译期就会报错 invalid operation: x == y (slice can only be compared to nil)
x := []int{1, 2, 3}
y := []int{1, 2, 3}

fmt.Println(x == y)

如果将 x 和 y 的值赋值给接口,再比较 2 个接口的值:

// 运行时报错 panic: runtime error: comparing uncomparable type []int
x := []int{1, 2, 3}
y := []int{1, 2, 3}

var xx interface{} = x
var yy interface{} = y
fmt.Println(xx == yy)

这样一来,接口值的比较是一件相当危险的操作,因为错误会发生在运行期。

我们再来看一个例子:

package main

import "fmt"

func main() {

    var x interface{} = []int{1, 2, 3}
    var y interface{} = [3]int{1, 2, 3}

    fmt.Println(x == y) // Output: false
}

上面的情况很惊险,没有报 panic 错误,竟然通过了。不过一得『归功于』 x 和 y 的 type 不同,因此返回了 false. 这是相当危险的操作。

4. 总结

  • 掌握接口值的构成
  • 掌握接口值比较

本文中的例子使用的接口都是空接口,你可以换一个非空接口试试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值