繁体   English   中英

满足接口的 Go 结构体方法的类型

[英]Types of Go struct methods that satisfy an interface

给出以下 Go 代码示例:

package main

import "fmt"

type greeter interface {
    hello()
    goodbye()
}

type tourGuide struct {
    name string
}

func (t tourGuide) hello() {
    fmt.Println("Hello", t.name)
}

func (t *tourGuide) goodbye() {
    fmt.Println("Goodbye", t.name)
}

func main() {
    var t1 tourGuide = tourGuide{"James"}
    t1.hello()   // Hello James
    t1.goodbye() // Goodbye James (same as (&t1).goodbye())

    var t2 *tourGuide = &tourGuide{"Smith"}
    t2.hello()   // Hello Smith
    t2.goodbye() // Goodbye Smith (same as (*t2).hello())

    // illegal: t1 is not assignable to g1 (why?)
    // var g1 greeter = t1

    var g2 greeter = t2
    g2.hello()   // Hello Smith
    g2.goodbye() // Goodbye Smith
}

我可以使用类型为 tourGuide t1的变量或指向 tourGuide t2的指针来调用结构体tourGuide的两种方法。 换句话说,我可以使用T*T类型的变量调用带有T接收器的方法。 类似地,我可以调用一个方法*T接收机使用类型的变量T (如果T可寻址的)或*T 我知道编译器处理这里的差异(请参阅我在代码中的注释)。

但是,当我们实现接口时,情况会发生变化。 在上面的代码中,一个类型为greeter接口的变量可以从一个指向tourGuide的指针tourGuide但不能从一个tourGuide

谁能告诉我为什么会这样? 为什么我可以调用t1.hello()t1.goodbye()但不知何故t1对接口greeter是不够的?

如果一个方法有一个指针接收器,那么只有一个指针值可以用作接收器值。 因此,要对某个值调用此方法,该值本身必须是一个指针,或者必须可以获取其地址(用作接收器)。

例如,如果您有一个变量,它是可寻址的,因此可以获取其地址并将其用作接收器。 规范允许你这样做,这会自动发生。

封装在接口中的值不可寻址。 创建接口值时,将复制包装在接口中的值。 因此无法获取其地址。 从理论上讲,您可以允许获取副本的地址,但这将是(甚至)比它提供的好处更多的混乱的根源,因为地址将指向副本,并且具有指针接收器的方法只能修改副本而不是原版。

请参阅此答案,其中详细说明/证明在创建接口值时复制了值: 切片如何包含自身?

如果您有一个指向结构的指针,那么 go 将允许您访问结构上的属性及其具有值类型接收器(与指针接收器相关)的函数,而无需取消引用您的指针,但这仅适用于一级指针,请参阅下面的代码,其中我将 t2 转换为指向tourguide的指针,此时我需要明确取消引用它以使其重新成为指向tourguide的指针。 将指向结构的指针的第一级视为一种特殊情况,它允许您使用语法糖来访问值类型的属性和函数,从而不必经常手动取消对变量的引用。

package main

import "fmt"

type greeter interface {
    hello()
    goodbye()
}

type tourGuide struct {
    name string
}

func (t tourGuide) hello() {
    fmt.Println("Hello", t.name)
}

func (t *tourGuide) goodbye() {
    fmt.Println("Goodbye", t.name)
}

func main() {
    var t1 tourGuide = tourGuide{"James"}
    t1.hello()   // Hello James
    t1.goodbye() // Goodbye James (same as (&t1).goodbye())

    var tmpT2 *tourGuide = &tourGuide{"Smith"}
    var t2 **tourGuide = &tmpT2
    (*t2).hello()   // Hello Smith
    (*t2).goodbye() // Goodbye Smith (same as (*t2).hello())

    //illegal: t1 is not assignable to g1 (why?)
    //var g1 greeter = t1

    //now this is illegal too
    //var g2 greeter = t2

    var g3 greeter = (*t2)
    g3.hello()   // Hello Smith
    g3.goodbye() // Goodbye Smith
}

我在这里的回答解释了为什么 Go 会阻止您获取存储在接口中的值的地址。

TL;博士其因为一个指向其中,当不同类型B的值随后被存储在接口指向的接口的类型A的值将被无效。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM