了解嵌入Go

时间:2014-04-14 01:04:57

标签: go embedding subclassing

我正在尝试了解Google Go的嵌入机制(作为子类化的替代方法)。下面是一个简单的程序,总结了我的方法问题:

package main

import "fmt"

type Person struct {
    Name string
}

func (p *Person) Talk() {
    fmt.Println("Hi, my name is Person")
}

func (p *Person) TalkVia() {
    fmt.Println("TalkVia ->")
    p.Talk()
}

type Android struct {
    Person
}

func (p *Android) Talk() {
    fmt.Println("Hi, my name is Android")
}

func main() {
    fmt.Println("Person")
    p := new(Person)
    p.Talk()
    p.TalkVia()

    fmt.Println("Android")
    a := new(Android)
    a.Talk()
    a.TalkVia()
}

输出结果为:

Person
Hi, my name is Person
TalkVia ->
Hi, my name is Person
Android
Hi, my name is Android
TalkVia ->
Hi, my name is Person

但是如果我是子类(用另一种语言),输出将是:

Person
Hi, my name is Person
TalkVia ->
Hi, my name is Person
Android
Hi, my name is Android
TalkVia ->
Hi, my name is Android

有没有办法通过google go embedding(不是接口)实现最后一个输出?

2 个答案:

答案 0 :(得分:6)

没有。嵌入是一种单向的方式。嵌入有点过于独特,不能简单地称之为“语法糖” - 它可以用来满足接口。 Android应该满足界面

type TalkViaer interface {
    TalkVia()
}

因为它嵌入了一个人。但是,从本质上讲,您必须记住嵌入只是一种非常聪明的方式来访问struct的成员。没有更多或更少。当p传递到TalkVia时,它会获得一个人,并且因为该人没有其所有者的概念,所以它将无法引用其所有者。

您可以通过在Person中保留一些所有者变量来解决此问题,但嵌入不是继承。根本没有关于“超级”或“扩展者”或类似内容的概念。这只是为结构提供某种方法集的一种非常方便的方法。


编辑:也许还有一点解释。但只是一点点。

type Android struct {
    P person
}

我们都同意,如果我a := Android{}然后a.P.TalkVia(),它就不会调用Android的任何方法,对吧?即使那是Java或C ++,这也没有意义,因为它是一个成员。

嵌入仍然只是一个成员。它只是Android拥有的一块数据,不多也不少。在句法层面,它将所有方法赋予Android,但它仍然只是一个成员,你无法改变它。

答案 1 :(得分:1)

我认为interface会更接近你想要实现的内容,而不是嵌入,我知道这不是你提出的问题。通过提供接口,两个Person和Android stucturs可以实现满足接口的reiver方法。 TalkVia将是添加到接口的下一个方法,然后可以提供所需的输出。

 type Talker interface {
       Talk()
 }
 ...
 func (p *Android) Talk() {
       fmt.Println("Hi, my name is ", p.Name )
 }
 ...
 func main() {
       p := Person { "Person" }
       p.Talk()
       p.TalkVia()
 }

基于您的代码的完整示例,其中的界面位于Go Playground Example

根据您关于无法修改Person的实现的评论。我用构造函数和嵌入式结构修改了示例,以生成以下内容:

Person
Hi, my name is Person
TalkVia ->
Hi, my name is Person
Android
Hi, my name is Android
Hi, my name is Android
TalkVia ->
Hi, my name is Android

完整示例在此second play ground example中,但简短形式执行以下操作:

type Android struct {
    Person
    Name string
}
...
func NewAndroid(name string) Android {
    return Android { Person { name }, name }
}

现在我们可以创建一个Android并将其用作Android和Person。实际上因为它现在嵌入了实现Talker接口的Person,所以它也可以直接调用TalkVia方法

func main() {
     ...

     a := NewAndroid("Android")
     a.Talk()
     a.TalkVia()
     ap := a.Person
     ap.Talk()
     ap.TalkVia()
}