Golang结构体在方法被重载时调用嵌入类型的方法。

huangapple go评论89阅读模式
英文:

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接口的值,整个面向对象的概念也被破坏了。传递给barI接口的对象指针部分不需要与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.

huangapple
  • 本文由 发表于 2014年1月1日 02:31:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/20861786.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定