我正在尝试了解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(不是接口)实现最后一个输出?
答案 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()
}