理解Go中的嵌入

理解Go中的嵌入,go,embedding,subclassing,Go,Embedding,Subclassing,我试图理解GoogleGo的嵌入机制(作为子类化的替代方案)。下面是一个简单的程序,总结了我在该方法中遇到的问题: 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

我试图理解GoogleGo的嵌入机制(作为子类化的替代方案)。下面是一个简单的程序,总结了我在该方法中遇到的问题:

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嵌入(而不是界面)来实现最后的输出?

没有。嵌入是一种单向的方法。嵌入有点太独特了,不能仅仅称之为“语法糖”——它可以用来满足接口。Android应该满足这个界面

type TalkViaer interface {
    TalkVia()
}
因为它嵌入了一个人。然而,从本质上讲,您必须记住嵌入只是一种非常聪明的方式,可以访问结构的成员。差不多没什么。当
p
被传递到
TalkVia
时,它会得到一个Person,因为这个Person不知道它的所有者,所以它将无法引用它的所有者

您可以通过在
Person
中保存一些所有者变量来解决这个问题,但嵌入不是继承。根本没有“超级”或“扩展器”之类的概念。这只是一种非常方便的方法,可以为结构体提供特定的方法集


编辑:也许需要更多的解释。但只是一点点

type Android struct {
    P person
}
我们都同意,如果我做了
a:=Android{}
,然后
a.p.TalkVia()
它就不会调用任何Android的方法,对吗?即使是java或C++,那也没什么意义,因为它是一个成员。


嵌入仍然只是一个成员。这只是安卓系统拥有的一部分数据,不多也不少。在语法层面上,它将其所有方法授予Android,但它仍然只是一个成员,你不能改变它。

不。嵌入是单向的。嵌入有点太独特了,不能仅仅称之为“语法糖”——它可以用来满足接口。Android应该满足这个界面

type TalkViaer interface {
    TalkVia()
}
因为它嵌入了一个人。然而,从本质上讲,您必须记住嵌入只是一种非常聪明的方式,可以访问结构的成员。差不多没什么。当
p
被传递到
TalkVia
时,它会得到一个Person,因为这个Person不知道它的所有者,所以它将无法引用它的所有者

您可以通过在
Person
中保存一些所有者变量来解决这个问题,但嵌入不是继承。根本没有“超级”或“扩展器”之类的概念。这只是一种非常方便的方法,可以为结构体提供特定的方法集


编辑:也许需要更多的解释。但只是一点点

type Android struct {
    P person
}
我们都同意,如果我做了
a:=Android{}
,然后
a.p.TalkVia()
它就不会调用任何Android的方法,对吗?即使是java或C++,那也没什么意义,因为它是一个成员。


嵌入仍然只是一个成员。这只是安卓系统拥有的一部分数据,不多也不少。在语法层面上,它将其所有方法授予Android,但它仍然只是一个成员,你不能改变这一点。

我认为
接口将更接近你想要实现的,而不是嵌入,我知道这不是你的问题。通过提供一个接口,两个人和Android结构可以实现满足该接口的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()
 }
基于您的代码的完整示例,在

基于您关于无法修改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
type Android struct {
    Person
    Name string
}
...
func NewAndroid(name string) Android {
    return Android { Person { name }, name }
}
完整示例如下所示,但简言之,其功能如下:

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
type Android struct {
    Person
    Name string
}
...
func NewAndroid(name string) Android {
    return Android { Person { name }, name }
}
现在我们可以创建一个安卓系统,并将其用作安卓系统和个人。事实上,因为它现在嵌入了实现Talker接口的Person,所以它也可以直接调用
TalkVia
方法

func main() {
     ...

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

我认为
接口
将更接近您想要实现的内容,而不是嵌入,我知道这不是您的问题所提出的内容。通过提供一个接口,两个人和Android结构可以实现满足该接口的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()
 }
基于您的代码的完整示例,在

基于您关于无法修改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
type Android struct {
    Person
    Name string
}
...
func NewAndroid(name string) Android {
    return Android { Person { name }, name }
}
完整示例如下所示,但简言之,其功能如下:

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
type Android struct {
    Person
    Name string
}
...
func NewAndroid(name string) Android {
    return Android { Person { name }, name }
}
现在我们可以创建一个安卓系统,并将其用作安卓系统和个人。事实上,因为它现在嵌入了实现Talker接口的Person,所以它也可以直接调用
TalkVia
方法

func main() {
     ...

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

当您无法修改上游代码但希望重用某些函数时,您可以做什么。在这种情况下,您使用定义Person(带有Talk和TalkVia)的包;你想定义Android重用TalkVia但重新实现Talk。我明白你的意思了。我已经修改了第二个示例,它能够生成您想要的输出。当您无法修改上游代码但希望重用某些函数时,您可以做什么。在这种情况下,您使用定义Person(带有Talk和TalkVia)的包;你想定义Android重用TalkVia但重新实现Talk。我明白你的意思了。我已经修改了第二个示例,它能够生成您想要的输出。嵌入不是子类化的替代方法。编程时别再考虑子类化了:你的设计会更好。@Volker:我同意。但是,我试图理解如何重用上游项目中的代码。我需要更改一个(很多)方法,但是没有接口(与