英文:
Golang struct calling embedded type methods when method has been overloaded
问题
我正在尝试学习Go语言,并找到了一个很好的资源这里。
下面是关于方法重载的示例代码:
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Employee struct {
Human
company string
}
func (h *Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func (e *Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone) //Yes you can split into 2 lines here.
}
func main() {
sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
sam.SayHi()
}
是否可以调用"基础"结构体(Human)的方法,例如sam.Human.SayHi()
?向下转型是不起作用的(因为没有类型层次结构,对吗?)
英文:
I am trying to learn Go, and I found a good resource here.
The example given on method overloading is reproduced below:
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Employee struct {
Human
company string
}
func (h *Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func (e *Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone) //Yes you can split into 2 lines here.
}
func main() {
sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
sam.SayHi()
}
Is it possible to call the "base" struct's (Human's) methods, eg. sam.Human.SayHi() Downcasting doesn't work (because there is no type hierarchy right?)
答案1
得分: 94
你可以通过使用父结构的成员名称来访问父结构的嵌入结构。这可能有点拗口,所以最好通过示例来演示。
sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
sam.SayHi() // 调用 Employee.SayHi
sam.Human.SayHi() // 调用 Human.SayHi
输出结果:
你好,我是 Sam,我在 Golang Inc 工作。你可以拨打我的电话号码 111-888-XXXX。
你好,我是 Sam,你可以拨打我的电话号码 111-888-XXXX。
英文:
You can access the embedded struct of a parent struct by calling the member of the parent with the name of the embedded type's name. That's a mouthful, so it's probably easier to demonstrate it.
sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
sam.SayHi() // calls Employee.SayHi
sam.Human.SayHi() // calls Human.SayHi
Outputs
Hi, I am Sam, I work at Golang Inc. Call me on 111-888-XXXX
Hi, I am Sam you can call me on 111-888-XXXX
答案2
得分: -1
这是我迄今为止找到的最接近正常多态性的近似解,它同时具有普通和纯虚函数。由于Go语言的设计本质和目标,它虽然不太美观但却很有效。
在真正的面向对象语言中,具有任何虚函数的类的每个对象都必须有一个指向虚函数表或至少映射到虚函数表的类型的指针。因此,将接口成员添加到基础(嵌入)结构中只会浪费一个额外的机器字,它只会指向自身。
或者,我们可以从A
中删除I
接口成员,并使纯虚成员函数只接受实现作为参数。
type I interface {
foo(s string)
bar(i I)
}
type A struct {}
type B struct {A}
type C struct {B}
func (this *A) bar(i I) {
i.foo("world")
}
https://play.golang.org/p/9gvaCuqmHS8
但是此时,foo
不再是纯虚函数,API用户可以传递任何类型实现了I
接口的值,整个面向对象的概念也被破坏了。传递给bar
的I
接口的对象指针部分不需要与this
相同,但是我们确实不需要使用init()
函数传递相同的值,但至少只有在同一个包中的API用户才被允许设置它。
此时,我们已经过渡到了Go的做事方式:组合式编程与依赖注入。这正是Ken Thompson和其他设计者认为更好的方式——至少对于他们所述的目标而言。虽然在许多方面它远远不如其他语言,但它确实带来了许多优势,我不会在这里讨论这些优点。
英文:
This is the nearest approximation to sane polymorphism with both plain and pure virtual functions that I have found thus far. By the very nature of Go's design and the goal at hand, it is ugly but effective.
package main
import (
"fmt"
)
type I interface {
foo(s string) // Our "pure virtual" function
bar()
}
type A struct {i I}
type B struct {A}
type C struct {B}
// fk receivers, this is a "member function" so I'll use OO nomenclature
func (this *A) init(i I) {
this.i = i // the i contains (internal) pointers to both an object and a type
}
func (this *A) bar() {
this.i.foo("world")
}
func (this *B) foo(s string) {
fmt.Printf("hello %s\n", s)
}
func (this *C) foo(s string) {
fmt.Printf("goodbye cruel %s\n", s)
}
func main() {
var i I
b := &B{}
b.init(b) // passing b as the parameter implicitly casts it to an I interface object
b.bar()
c := &C{}
c.init(c)
c.bar() // c is a pointer to C, so Golang calls the correct receiver
i = b
i.bar()
i = c
i.bar() // Internally, i contains pointers to the C object and the C type,
// so that the correct receiver is called
}
https://play.golang.org/p/4qBfmJgyuHC
In real OO languages, each object of a class with any virtual functions must have a pointer to a virtual function table or a least type that maps to it. So adding an interface member to the base (embedded) struct only wastes an additional machine word for the pointer that will just point to its self.
Alternatively, we could remove the I
interface member from A
and the have pure virtual member function just accept the implementation as an argument.
type I interface {
foo(s string)
bar(i I)
}
type A struct {}
type B struct {A}
type C struct {B}
func (this *A) bar(i I) {
i.foo("world")
}
https://play.golang.org/p/9gvaCuqmHS8
But at this point, foo
is no longer a pure virtual function, API users could pass any value whose type implements I
, and the whole OO concept is broken. The object pointer portion of the I
interface passed to bar doesn't need to be the same as this
, but then again we didn't need to pass that same value using an init()
function, but at least only an API user in the same package would be allowed to set it.
At this point, we have transitioned to the Go way of doing things: compositional programming with dependency injection. This is what Ken Thompson and the other designers thought was a better way to go -- at least for their stated goals. While it is vastly inferior in MANY respects, it does create many advantages and I won't argue those points that here.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论