猿问

Golang 中的空接口

这不是在 Go 中使用接口的正确方法。这个问题的目的是让我了解 Go 中的空接口是如何工作的。


如果 Go 中的所有类型都实现interface{}(空接口),为什么我不能访问and结构name中的字段?如何通过函数 sayHi() 访问每个结构的名称字段?CatDog


package main


import (

    "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

}


红糖糍粑
浏览 140回答 3
3回答

慕村225694

Aninterface{}是方法集,而不是字段集。如果一个类型的方法包含该接口的方法,则该类型实现该接口。由于空接口没有任何方法,所有类型都实现它。如果你需要访问一个字段,你必须得到原始类型:name, ok:=i.(Dog).name如果i是 a ,这将恢复名称Dog。或者,为和实现一个getName()函数,然后它们都将实现以下接口:DogCattype NamedType interface {   getName() string}然后你可以将你的函数重写为:func sayHi(i NamedType) {   fmt.Println(i.getName()) }

手掌心

你不能这样做,因为接口值不这样做。接口值做什么——不管接口类型本身;接口类型是否为空并不重要——它们有两个东西:某个值的具体类型(或无类型);和该具体类型的值(或无值)。因此,如果某个变量v或表达式e的类型I是I接口类型,那么您可以使用某种语法检查这两个“字段”中的一个或两个。它们不是struct字段,因此您不能只使用v.type,但您可以这样做:switch v.(type) {case int: // the value in v has type intcase *float64: // the value in v has type float64// etc}这意味着让我看.(type)一下类型字段。switch获取实际值更难,因为 Go 或多或少需要您先检查类型。在您的情况下,您知道i包含 aDog或 a Cat,因此您可以编写:var name stringswitch i.(type) {case Dog: name = i.(Dog).namecase Cat: name = i.(Cat).namedefault: 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 *intif i == nil {    fmt.Println("i is initially nil")}if p == nil {    fmt.Println("p is nil")}i = pif 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。)

catspeake

就像你说的,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}就是这样。
随时随地看视频慕课网APP

相关分类

Go
我要回答