《Go in Action 2015.11.pdf》之Go’s type system

本文探讨了Go语言中的接口和类型嵌入。在接口使用中,解释了为何传递值给接口时,只能调用接收者为值的方法,而传递指针则可以调用值或指针方法。在类型嵌入部分,展示了如何通过结构体嵌入已有类型来扩展或改变行为,以及如何在嵌入指针类型时调用方法。通过示例,展示了如何在不直接定义方法的情况下,使新类型继承并可能修改已有类型的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Interface

type notifier interface {
    notify()
}

type user struct {
    name  string
    email string
}

func (u *user) notify() {
    fmt.Printf("sending %v %v", u.email, u.name)
}


func sendNotification(n notifier) {
    n.notify()
}
func main() {
    u := user{name: "name", email: "email"}
    sendNotification(&u)
}

代码中user实现了接口notify,但是编译执行时报错

cannot use u (type user) as type notifier in argument to sendNotification:
    user does not implement notifier (notify method has pointer receiver)

为什么会这样呢?让我们看一下golang规范中的描述:

ValuesMethods Receivers
T(t T)
*T(t T) and (t *T)

表格的意思是,如果给interface传入的是值,那么只能调用Receiver是值的Methods,如果给interface传入的是指针,那么能调用Receiver是值的Methods或指针的Methods.

表格反过来表示

Methods ReceiversValues
*T(t *T)
T(t T) and (t *T)

如果Receiver是指针,传递给interface的参数只能是指针,如果Receiver是指,那么传递给interface的参数可以是指针或指。

之所以有这样的限制在于并非所有的指都是可以取地址的。


type duration int

func (d *duration) pretty() string {
    return fmt.Sprintf("durantion:%d", *d)
}

func main() {
    duration(23).pretty()
}

程序会报错:

.\main.go:60: cannot call pointer method on duration(23)
.\main.go:60: cannot take the address of duration(23)

duration(23)是无法取地址的。

Type embedding

golang允许使用已有的类型并扩展或改变已有类型的行为,通过Type embedding实现。Type embedding通过在struct 中嵌入已有类型实现。新的类型拥有被嵌入类型的所有成员和方法,而且可以在struct中扩展或改变被嵌入类型的方法。

type user struct {
    name  string
    email string
}

func (u *user) notify() {
    fmt.Printf("sending %v %v\n", u.email, u.name)
}

type admin struct {
    user
    lever string
}

func main() {
    ad := admin{
        user: user{
            name:  "jonh",
            email: "sss@ss",
        },
        lever: "super",
    }
    ad.user.notify()
    ad.notify()
}

结果

sending sss@ss jonh
sending sss@ss jonh

可以看到admin虽然没定义方法notify(),但是拥有了user的notify()方法。
加上interface

type notifier interface {
    notify()
}

type user struct {
    name  string
    email string
}

func (u *user) notify() {
    fmt.Printf("sending %v %v\n", u.email, u.name)
}

type admin struct {
    user
    lever string
}

func sendNotification(n notifier) {
    n.notify()
}
func main() {
    ad := admin{
        user: user{
            name:  "jonh",
            email: "sss@ss",
        },
        lever: "super",
    }
    ad.user.notify()
    ad.notify()
    sendNotification(&ad)
}

结果

sending sss@ss jonh
sending sss@ss jonh
sending sss@ss jonh

如果admin要更改notify的话:
添加

func (u *admin) notify() {
    fmt.Printf("admin sending %v %v\n", u.email, u.name)
}

结果:

sending sss@ss jonh
admin sending sss@ss jonh
admin sending sss@ss jonh

可以看到notify已经更改,不过可以通过ad.user.notify()访问内部类型的方法。

type embedding for pointer

我们还可以将类型的指针作为嵌入类型。


type padmin struct {
    *user
}

func main() {
    ad := padmin{
        user: &user{
            name:  "wdy",
            email: "000",
        },
    }
    ad.user.notify()
    ad.notify()
    sendNotification(&ad)
    sendNotification(ad)
}

运行后结果:

sending 000 wdy
sending 000 wdy
sending 000 wdy
sending 000 wdy

此时我们注意到,可以给函数sendNotification直接传入ad或&ad。

    sendNotification(&ad)
    sendNotification(ad)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值