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

Golang 中的空接口

Golang 中的空接口

Go
红糖糍粑 2022-05-10 16:10:43
这不是在 Go 中使用接口的正确方法。这个问题的目的是让我了解 Go 中的空接口是如何工作的。如果 Go 中的所有类型都实现interface{}(空接口),为什么我不能访问and结构name中的字段?如何通过函数 sayHi() 访问每个结构的名称字段?CatDogpackage mainimport (    "fmt")func sayHi(i interface{}) {    fmt.Println(i, "says hello")    // Not understanding this error message    fmt.Println(i.name) //  i.name undefined (type interface {} is interface with no methods)} type Dog struct{    name string}type Cat struct{    name string}func main() {    d := Dog{"Sparky"}    c := Cat{"Garfield"}    sayHi(d) // {Sparky} says hello    sayHi(c) // {Garfield} says hello}
查看完整描述

3 回答

?
慕村225694

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

Aninterface{}是方法集,而不是字段集。如果一个类型的方法包含该接口的方法,则该类型实现该接口。由于空接口没有任何方法,所有类型都实现它。


如果你需要访问一个字段,你必须得到原始类型:


name, ok:=i.(Dog).name

如果i是 a ,这将恢复名称Dog。


或者,为和实现一个getName()函数,然后它们都将实现以下接口:DogCat


type NamedType interface {

   getName() string

}

然后你可以将你的函数重写为:


func sayHi(i NamedType) {

   fmt.Println(i.getName()) 

}


查看完整回答
反对 回复 2022-05-10
?
手掌心

TA贡献1942条经验 获得超3个赞

你不能这样做,因为接口值不这样做。


接口值做什么——不管接口类型本身;接口类型是否为空并不重要——它们有两个东西:


某个值的具体类型(或无类型);和

该具体类型的值(或无值)。

因此,如果某个变量v或表达式e的类型I是I接口类型,那么您可以使用某种语法检查这两个“字段”中的一个或两个。它们不是struct字段,因此您不能只使用v.type,但您可以这样做:


switch v.(type) {

case int: // the value in v has type int

case *float64: // the value in v has type float64

// etc

}

这意味着让我看.(type)一下类型字段。switch


获取实际值更难,因为 Go 或多或少需要您先检查类型。在您的情况下,您知道i包含 aDog或 a Cat,因此您可以编写:


var name string

switch i.(type) {

case Dog: name = i.(Dog).name

case Cat: name = i.(Cat).name

default: panic("whatever 'i' is, it is not a Dog or Cat")

}

fmt.Println(name)

这很笨拙,有很多方法可以让它不那么笨拙,但这始终是第一步:弄清楚类型是什么。


好吧,有时在第一步之前有一个步骤:弄清楚变量中是否有任何东西。 你这样做:


if i == nil {

    ...

}

但是请注意,如果i其中有一些类型化的值,并且该类型可以保存 nil 指针,则value部分i可以为 nil,但仍为i == nilfalse。那是因为i 它确实有一个类型。


var i interface{}

var p *int

if i == nil {

    fmt.Println("i is initially nil")

}

if p == nil {

    fmt.Println("p is nil")

}

i = p

if i != nil {

    fmt.Printf("i is now not nil, even though i.(*int) is %v\n", i.(*int))

}

(在Go 操场上试试这个)。


这通常不是正确的使用方式interface

大多数情况下——也有例外——我们甚至不会尝试查看某个接口的类型。相反,我们定义了一个接口,它提供了方法——我们可以调用的函数——来做我们需要做的事情。请参阅Burak Serdar 的答案,其中接口类型有一个getName方法。然后,与其试图找出某人给了我们一些有限的类型中的哪一种,我们只是说:


name := i.getName()

调用底层具体值的getName 方法。如果i持有 a Dog,则调用func (Dog) getName() string,您需要对其进行定义。如果i持有 a Cat,则调用func (Cat) getName() string。如果您决定将一个名为 的类型添加到您的集合中Bird,您可以定义func (Bird) getName() string,依此类推。


(通常,这些方法也会被导出:GetName,而不是getName。)


查看完整回答
反对 回复 2022-05-10
?
catspeake

TA贡献1111条经验 获得超0个赞

就像你说的,interface{}是一个空的界面。你怎么能假设“空”的东西name里面有一个字段(fmt.Println(i.name))?你不能。事实上,go 不支持接口中的字段,只支持方法。


你可以做的(当然有很多解决方案)是定义一个接口(我们称之为它Pet),它有一个返回宠物名字的方法:


type Pet interface {

    getName() string

}

然后你可以在函数中接收这个接口(它的对象)sayHi并用它来打印宠物的名字:


func sayHi(i Pet) {

    fmt.Println(i.getName())

}

现在,为了能够传递Dog或Cat,sayHi()这两个结构都必须实现接口。因此,getName()为它们定义方法:


func (d Dog) getName() string {

    return d.name

}


func (c Cat) getName() string {

    return c.name

}

就是这样。


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

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信