为了账号安全,请及时绑定邮箱和手机立即绑定

接口可以是 nil 类型,但这并不意味着它们是 nil?

接口可以是 nil 类型,但这并不意味着它们是 nil?

Go
绝地无双 2022-08-01 16:56:19
我写了一些代码,实际上就是这样做的:package mainimport "fmt"type SomeInterface interface {    Retrieve(identifier string)}type SomeStruct struct {}func (r SomeStruct) Retrieve(identifier string) {    fmt.Println("identifier ", identifier)}type Handler struct {    Name string    SomeObject SomeInterface}func main() {    var someStruct *SomeStruct    var h = Handler{        Name: "helo",        SomeObject: someStruct,    }    fmt.Printf("before %+v\r\n", h.SomeObject)    if h.SomeObject == nil {        fmt.Printf("during %+v\r\n", h.SomeObject)    }    fmt.Printf("after %+v\r\n", h.SomeObject)}请有人向我解释为什么上面的输出是:before <nil>after <nil>我一直在阅读有关类型的接口的信息,但在这种情况下,我将接口分配给了一个尚未分配的指针,因此我本来以为该接口并且我会看到 - 唉,事实并非如此。nil== nilduring <nil>
查看完整描述

3 回答

?
凤凰求蛊

TA贡献1825条经验 获得超4个赞

接口值是一个简单的数据结构,由两部分组成:类型和基础值。因此,接口值本身可以是 ,或者接口值可以存在,但基础值可以是 。例如:nilnil


var x interface{} = nil // x is nil

var y interface{} = (interface{})(nil) // y is a interface{}, which *contains* nil

这在某些概念上与此差异相似:


var x []*int = nil // x is nil

var y []*int = []*int{nil} // y is a []*int, which *contains* nil

fmt.Printf由于接口格式化输出的方式,掩盖了接口情况下的差异;如果您愿意,可以使用反射更清楚地看到差异。


查看完整回答
反对 回复 2022-08-01
?
眼眸繁星

TA贡献1873条经验 获得超9个赞

在 Go 中,引用某些接口的实现者的变量可以具有多种类型。它可以是类型(是的,可以描述类型以及值),它可以是其实现者之一的类型,也可以是指向其实现者之一的指针的类型。默认情况下,引用接口的变量的类型为 。一旦你向它分配了一些东西(除了它本身),它就会采用你分配给它的东西的类型(同样,要么是它的实现者之一的类型,要么是指向其中一种类型的指针)。<nil>nilnilnil


可以使用 打印接口变量的类型,并使用 %v 打印其值:%T


func main() {

    var i SomeInterface

    fmt.Printf("%T, %v\n", i, i) // Prints <nil>, <nil>

    

    var someStruct SomeStruct

    i = someStruct

    fmt.Printf("%T, %v\n", i, i) // Prints main.SomeStruct, {}

    

    var someStructPtr *SomeStruct

    i = someStructPtr

    fmt.Printf("%T, %v\n", i, i) // Prints *main.SomeStruct, <nil>

}

现在,每当您比较时,只有当两个操作数的类型和值都匹配时,比较才会被计算为 true。在你的情况下,的值显然是(毕竟,的值肯定是 ,并且你将其值存储在 中)。的类型,根据我刚才解释的内容,必须是 。的价值显然是。h.SomeObject == nilh.SomeObject<nil>someStruct<nil>h.SomeObjecth.SomeObject*SomeStructnil<nil>


但是,是什么类型?nil


好吧,可以采用多种类型,编译器必须决定每种用法应该采用哪种类型。当涉及到比较和分配时,它只是简单地采用它被比较或分配的东西的类型。例如,如果要将整数指针与 进行比较,则在这种情况下,类型为 。nilnilnil*int


但所有这些都必须在编译时决定,并且引用接口的变量可以在运行时更改类型。那么,当您将引用接口的变量与 进行比较时,在这种情况下,编译器会为操作数提供什么类型呢?好吧,它给了它唯一合理的类型, .nilnil<nil>


最后一个示例,请考虑以下代码:


func main() {

    var p *SomeStruct = nil // = nil is optional; pointers default to nil

    var i SomeInterface = p

    printf("%t\n", p == nil) // Prints true

    printf("%t\n", p == i) // Prints true

    printf("%t\n", i == nil) // Prints false

}

p == nil为 true,因为 is 的类型带有值 ,并且(在本例中)也是具有值的类型。p*SomeStruct<nil>nil*SomeStruct<nil>


p == i是 true,因为 也是具有值的类型(它只是存储 的类型和值)。i*SomeStruct<nil>p


但是,是 false,因为在这种情况下,采用的类型而不是 。i == nilnil<nil>*SomeStruct


解决这个问题的方法是永远不要在接口引用变量中存储一些有价值的东西,除了它本身。这样,每当接口引用变量的值为 时,其类型也将是 ,并且比较将按预期工作。因此,您经常会看到如下所示的代码:<nil>nil<nil><nil>nil


if p == nil {

    i = nil

} else {

    i = p

}


查看完整回答
反对 回复 2022-08-01
?
aluckdog

TA贡献1847条经验 获得超7个赞

SomeObject 不是 nil,它只是指向 SomeStruct,它是 nil。

我认为混乱是fmt。Printf打印这种情况,因为它遵循指针,最终结果是零。<nil>


查看完整回答
反对 回复 2022-08-01
  • 3 回答
  • 0 关注
  • 157 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号