How to do simple inheritance in Go

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

How to do simple inheritance in Go

问题

我是一个Python开发者,正在尝试学习Go语言。目前我正在尝试重构我的第一个小项目,但是我不太确定如何在结构体之间共享一个方法。

简而言之,你想在Go语言中实现类似于以下Python代码的功能:

class Super(object):

  def CommonMethod(self):
    print 'I am the common method.'

class One(Super):

  def MethodOne(self):
    self.CommonMethod()
    print 'I am method one.'

class Two(Super):

  def MethodTwo(self):
    self.CommonMethod()
    print 'I am method two.'

one = One()
one.MethodOne()

two = Two()
two.MethodTwo()

请注意,Go语言中没有类的概念,而是使用结构体和方法来实现类似的功能。你可以通过在结构体中定义方法来实现方法的共享。以下是将上述Python代码转换为Go代码的示例:

package main

import "fmt"

type Super struct{}

func (s *Super) CommonMethod() {
    fmt.Println("I am the common method.")
}

type One struct {
    Super
}

func (o *One) MethodOne() {
    o.CommonMethod()
    fmt.Println("I am method one.")
}

type Two struct {
    Super
}

func (t *Two) MethodTwo() {
    t.CommonMethod()
    fmt.Println("I am method two.")
}

func main() {
    one := &One{}
    one.MethodOne()

    two := &Two{}
    two.MethodTwo()
}

希望这可以帮助到你!如果你有任何其他问题,请随时问我。

英文:

I'm a Python developer, trying to learn Go. Currently I'm trying to refactor my first small project, but I am not too sure how to share a method between structs.

Long story short, how would you do something like this Python code in Go?

class Super(object):

  def CommonMethod(self):
    print 'I am the common method.'


class One(Super):

  def MethodOne(self):
    self.CommonMethod()
    print 'I am method one.'


class Two(Super):

  def MethodTwo(self):
    self.CommonMethod()
    print 'I am method two.'

one = One()
one.MethodOne()

two = Two()
two.MethodTwo()

答案1

得分: 7

在Go语言中,方法并不像在其他语言(如Python或Java)中那样通过子类继承来自动继承。你可以定义接口并使用嵌入来实现类似的功能,但你需要为每种类型实现所需的方法。当然,你可以在外部方法中直接调用嵌入类型的方法,但要注意任何更改都将发生在内部对象而不是外部对象上。

根据文档:

嵌入与子类继承有一个重要的区别。当我们嵌入一个类型时,该类型的方法成为外部类型的方法,但当调用这些方法时,方法的接收者是内部类型而不是外部类型。在我们的示例中,当调用bufio.ReadWriterRead方法时,它与上面编写的转发方法具有完全相同的效果;接收者是ReadWriterreader字段,而不是ReadWriter本身。

更多信息:

以下是文档中的一些参考资料:

http://golang.org/doc/faq#Is_Go_an_object-oriented_language

Go是一种面向对象的语言吗?

是和否。虽然Go具有类型和方法,并允许面向对象的编程风格,但它没有类型层次结构。Go中的“接口”概念提供了一种不同的方法,我们认为它易于使用,并且在某些方面更通用。还有一些将类型嵌入到其他类型中以提供类似但不完全相同的子类化功能的方法。

因此,你可以定义接口来规定类型应该实现的方法,但你需要为每种类型实现这些方法。

另一个方便之处是嵌入:

http://golang.org/doc/effective_go.html#embedding

Go没有提供典型的、基于类型的子类化概念,但它确实可以通过在结构体或接口中嵌入类型来“借用”实现的部分。

接口嵌入非常简单。我们之前提到过io.Readerio.Writer接口;以下是它们的定义。

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

io包还导出了其他几个接口,用于指定可以实现多个这样的方法的对象。例如,有一个io.ReadWriter接口,它包含了ReadWrite方法。我们可以通过显式列出这两个方法来指定io.ReadWriter,但通过嵌入这两个接口来形成新接口更加简单和具有描述性,如下所示:

// ReadWriter是将Reader和Writer接口组合在一起的接口。
type ReadWriter interface {
    Reader
    Writer
}
英文:

TL;DR

In Go methods aren't just magically inherited with subclassing as you would do in other languages like Python or Java. You can define interfaces and using embedding but you'll have to implement the methods you need for each type. Of course you can just call the method of the embedded from the outer method, however be careful that any changes will occur to the inner object and not the outer one.

From the docs:

> There's an important way in which embedding differs from subclassing.
> When we embed a type, the methods of that type become methods of the
> outer type, but when they are invoked the receiver of the method is
> the inner type, not the outer one. In our example, when the Read
> method of a bufio.ReadWriter is invoked, it has exactly the same
> effect as the forwarding method written out above; the receiver is the
> reader field of the ReadWriter, not the ReadWriter itself.

Some more info

Here's some references from the docs:

http://golang.org/doc/faq#Is_Go_an_object-oriented_language
> Is Go an object-oriented language?
>
> Yes and no. Although Go has types and methods and allows an
> object-oriented style of programming, there is no type hierarchy. The
> concept of “interface” in Go provides a different approach that we
> believe is easy to use and in some ways more general. There are also
> ways to embed types in other types to provide something analogous—but
> not identical—to subclassing.

So, you can have interfaces that define what should be implemented in a type but you'll have to implement those methods for each type.

One convenience you have is Embedding:

http://golang.org/doc/effective_go.html#embedding

> Go does not provide the typical, type-driven notion of subclassing,
> but it does have the ability to “borrow” pieces of an implementation
> by embedding types within a struct or interface.
>
> Interface embedding is very simple. We've mentioned the io.Reader and
> io.Writer interfaces before; here are their definitions.

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
} 

> The io package also exports several other interfaces that specify
> objects that can implement several such methods. For instance, there
> is io.ReadWriter, an interface containing both Read and Write. We
> could specify io.ReadWriter by listing the two methods explicitly, but
> it's easier and more evocative to embed the two interfaces to form the
> new one, like this:

// ReadWriter is the interface that combines the Reader and Writer interfaces.
type ReadWriter interface {
    Reader
    Writer
}

答案2

得分: 3

注意:最近(2014年11月18日)有一篇名为《在Go中的子类化》的文章介绍了一些在Go中模仿Java或Python等“面向对象”语言行为的技巧。

虽然组合是常见的方式,但如果你需要在运行时解析对方法的调用,你需要让“Parent”类型具有对接口的引用。

请注意,这并不提供“真正”的继承(正如我在“[使用接口进行函数重载的Go语言][2]”中所解释的那样)。
更多信息请参见“[Go中的继承语义][3]”。

示例(play.golang.org):

type Person interface {
    close() // 定义一个接口以查找方法
    flush()
}
type Parent struct {
    self Person // 保留对自身的引用以进行动态调度
}
func (p *Parent) flush() {
    fmt.Println("parent flush")
}
func (p *Parent) close() {
    p.self.flush() // 调用子类的flush方法。
    fmt.Println("close")
}

这样你可以使用Child类型嵌入Parent及其`self`接口引用子类化parent

```go
type Child struct {
    Parent
}
func (c *Child) flush() {
    c.Parent.flush()
    fmt.Println("child flush")
}

这意味着如果你创建一个Child实例,并将该实例放入“self”中:

```go
x := new(Child)
x.self = x

那么对`x.close()`的调用将打印

parent flush
child flush
close


[1]: http://whitane.com/post/oo-in-go/
[2]: https://stackoverflow.com/a/25075999/6309
[3]: http://tech.t9i.in/2014/01/inheritance-semantics-in-go/
[4]: http://play.golang.org/p/jJ87KqMFCu

<details>
<summary>英文:</summary>

Note: a recent (today Nov 18th 2014) article called &quot;[Subclassing in Go][1]&quot; illustrates some techniques for mimicking behaviour of “oo” languages like java or python in go.

While composition is the norm, if you need a &quot;`Parent`&quot; type to resolve a call to a method *at runtime*, you would need said `Parent` type to have a reference to an interface.

Note that this doesn&#39;t provide &quot;true&quot; inheritance though (as I explained in &quot;[Function Over Loading in GO using interfaces][2]&quot;).  
For more see &quot;[Inheritance Semantics in Go][3]&quot;.

[Example (play.golang.org)][4]:

    type Person interface {
        close() // define an interface to lookup methods on
        flush()
    }
    type Parent struct {
        self Person // retain reference to self for dynamic dispatch
    }
    func (p *Parent) flush() {
        fmt.Println(&quot;parent flush&quot;)
    }
    func (p *Parent) close() {
        p.self.flush() // call the flush method of whatever the child is.
        fmt.Println(&quot;close&quot;)
    }

That way, you can &quot;subclass&quot; parent with a Child type (embedding Parent, and its `self` interface reference):

    type Child struct {
        Parent
    }
    func (c *Child) flush() {
        c.Parent.flush()
        fmt.Println(&quot;child flush&quot;)
    }

That means if you create a Child instance *and* put that instance in &quot;self&quot;:

    x := new(Child)
    x.self = x

Then a call to `x.close()` would print:

    parent flush
    child flush
    close


  [1]: http://whitane.com/post/oo-in-go/
  [2]: https://stackoverflow.com/a/25075999/6309
  [3]: http://tech.t9i.in/2014/01/inheritance-semantics-in-go/
  [4]: http://play.golang.org/p/jJ87KqMFCu

</details>



# 答案3
**得分**: 1

根据twotwotwo在我的问题上的评论,我提出了以下在Go语言中的解决方案:

Go playground链接:http://play.golang.org/p/JtPAON93PO

```go
type Super struct{}

type One struct {
    Super *Super
}

type Two struct {
    Super *Super
}

func (s *Super) CommonMethod() {
    fmt.Println("我是公共方法。")
}

func (o *One) MethodOne() {
    o.Super.CommonMethod()
    fmt.Println("我是方法一。")
}

func (t *Two) MethodTwo() {
    t.Super.CommonMethod()
    fmt.Println("我是方法二。")
}

以上是代码的翻译部分。

英文:

Based on twotwotwo's comment on my question, I came up with the following solution on Go:

Go playground link: http://play.golang.org/p/JtPAON93PO

type Super struct{}

type One struct {
	Super *Super
}

type Two struct {
	Super *Super
}

func (s *Super) CommonMethod() {
	fmt.Println(&quot;I am the common method.&quot;)
}

func (o *One) MethodOne() {
	o.Super.CommonMethod()
	fmt.Println(&quot;I am method one.&quot;)
}

func (t *Two) MethodTwo() {
	t.Super.CommonMethod()
	fmt.Println(&quot;I am method two.&quot;)
}

huangapple
  • 本文由 发表于 2014年11月18日 09:42:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/26985191.html
匿名

发表评论

匿名网友

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

确定