首页 > golang > go语音结构体与接口
2020
07-12

go语音结构体与接口

常见结构体赋值给接口出现的错误

package main

import "fmt"

type Animal interface {
    Eat()
    Move()
}

type Human struct {
}
func (h *Human)Eat() {
    fmt.Println("Eat")
}
func (h Human)Move() {
    fmt.Println("Move")
}

func main() {
    var animal1 Animal
    animal1 = &Human{}
    animal1.Move()
    animal1.Eat()

    //这样却能调用
    h := Human{}
    h.Eat()
    h.Move()

    //这样却语法错误
    /**
    var animal Animal
    animal = Human{}
    animal.Move()
    animal.Eat()
    //cannot use Human{…} (value of type Human) as type Animal in assignment:
    //Human does not implement Animal (Eat method has pointer receiver)
    */
}

初学Go语言可能会比较迷惑,方法接受者可以是结构体或者结构体指针,接口变量可以赋值为结构体或者结构体指针。但是当遇到上面程序:animal赋值为结构体变量,Eat方法接收者为结构体指针,竟然编译错误,提示结构体Human没有实现接口Animal的方法,并且说明Eat方法接受者为结构体指针。而animal1变量赋值为结构体指针,却既能调用Eat方法,也能调用Move方法。为什么呢?


其实我们在定义了结构体Human后,Go语言不止定义了type."".Human一种类型,还定义了结构体指针类型,我们通过通过"go tool compile"看一下:

//结构体(指针)类型变量赋值给接口类型变量,自动创建对应itab类型
go.itab.*"".Human,"".Animal

type.*"".Human SRODATA
    rel 72+4 t=5 type..namedata.Eat.+0  //方法1
    rel 76+4 t=26 type.func()+0
    rel 80+4 t=26 "".(*Human).Eat+0
    rel 84+4 t=26 "".(*Human).Eat+0
    rel 88+4 t=5 type..namedata.Move.+0  //方法2
    rel 92+4 t=26 type.func()+0
    rel 96+4 t=26 "".(*Human).Move+0
    rel 100+4 t=26 "".(*Human).Move+0

type."".Human SRODATA
    rel 96+4 t=5 type..namedata.Move.+0  //方法1
    rel 100+4 t=26 type.func()+0
    rel 104+4 t=26 "".(*Human).Move+0
    rel 108+4 t=26 "".Human.Move+0

    这下明确了,结构体Human类型只有Move方法,而结构体Human指针类型有Eat以及Move方法;所以在向接口Animal类型赋值时,结构体变量无法编译通过。然而我们又发现,结构体变量h,却可以调用Eat以及Move方法,不是说结构体Human类型只有Move方法吗?其实这是编译阶段做了处理,将变量h的地址(也就是结构体Human指针类型)作为参数传递给Eat方法了。


    这一点要特别注意,方法接收者不管是结构体还是结构体指针,通过结构体变量或者结构体指针变量调用,都是没有问题的。但是,一旦赋值给接口类型变量,编译时会做类型检查,发现结构体类型没有实现某些方法,可是会导致语法错误的。

    

    再扩展思考一下为什么要这么设计呢?结构体变量赋值给接口类型变量,不是一样可以获取到该结构体地址呢?不同样可以调用Eat方法。为什么不设计成这样呢?原因其实上面已经解释过了,animal = Human{}方式赋值时,会将原始结构体变量拷贝一份副本,iface.data指向的是该副本数据,这时候获取到的地址,还是原始结构体变量的地址吗?


空接口常见错误

package main

import "fmt"

func main() {
    var a map[string]int = nil
    fmt.Println(a == nil)   //true
    test(a)
}

func test(v interface{}) {
    fmt.Println(v == nil)  //false
}

由于任何类型都能转化为interface{},nil转化之后还等于nil吗?刚开始写Go语言,老是搞不清楚,明明最初值是nil,作为interface{}类型传递到函数之后,再判断竟然不等于nil了!现在知道了,空接口interface{}对应的变量用eface表示,肯定是不会等于nil的。

本文》有 0 条评论

留下一个回复