Go "inheritance" – using anonymous type in a struct as a method parameter

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

Go "inheritance" - using anonymous type in a struct as a method parameter

问题

我正在尝试弄清楚Go提供的继承概念(可能更接近"组合"而不是纯粹的继承)。然而,我无法理解为什么我不能将"父类"类型作为func参数来生成一个作用于该参数的通用函数。

假设我的理解是错误的,我该如何在Go中实现我想要的效果?

编辑
注意:

  • 我不想将行为附加到结构体上。

  • 我希望保持指针类型作为方法参数,因为我在一个独立的项目上单独处理结构体,这要求我在对其进行操作之前对其进行修改。

  • 实际上,我的Dog结构体会有额外的字段/成员;希望这不会进一步混淆问题。

英文:

I'm trying to firm up the concept of inheritence that Go provides (rather "composition" than pure inheritence, perhaps). However, I'm failing to grasp why I can't use the "parent" type as a func parameter to produce a generic function that acts on the parameter.

package main

import "log"

type Animal struct {
	Colour string
	Name string
}

type Dog struct {
	Animal
}

func PrintColour(a *Animal) {
	log.Printf("%s\n", a.Colour)
}


func main () {
	a := new (Animal)
	a.Colour = "Void"
	d := new (Dog)
	d.Colour = "Black"
	
	PrintColour(a)
	PrintColour(d)
}

Assuming my understanding's incorrect, how can I achieve what I want in Go?

Edit
Note:

  • I don't want to attach the behaviour to the struct

  • I'd like to keep the pointer type as the method parameter because I'm working separately on a pet project and this requires I manipulate the struct passed in before then acting on it.

  • In reality my Dog struct would have additional fields/members; hopefully this doesn't muddy the water further

答案1

得分: 13

我喜欢目前这里的答案,并且我想添加一个允许你使用接口对传入的接口进行静态类型检查的方法:

package main

import (
	"fmt"
)

type Animalizer interface {
	GetColour() string
}

type Animal struct {
	Colour string
	Name   string
}

type Dog struct {
	Animal
}

func (a *Animal) GetColour() string {
	return a.Colour
}

func PrintColour(a Animalizer) {
	fmt.Print(a.GetColour())
}

func main() {
	a := new(Animal)
	a.Colour = "Void"
	d := new(Dog)
	d.Colour = "Black"

	PrintColour(a)
	PrintColour(d)
}

在这个示例中,你可以进一步添加Dog的字段。与Uriel的答案不同之处在于,如果传入的不是实现了Animalizer接口的结构体,调用PrintColour将在编译时失败。

此外,你不需要使用类型断言,因为编译器知道Animalizer实现了GetColour方法。

最后,行为(打印)与结构体分离,GetColour只返回颜色。

英文:

I like the answers here so far and I want to add one that allows you to do static type checking on the interface you pass in using an interface:

package main

import (
	"fmt"
)

type Animalizer interface {
	GetColour() string
}

type Animal struct {
	Colour string
	Name   string
}

type Dog struct {
	Animal
}

func (a *Animal) GetColour() string {
	return a.Colour
}

func PrintColour(a Animalizer) {
	fmt.Print(a.GetColour())
}

func main() {
	a := new(Animal)
	a.Colour = "Void"
	d := new(Dog)
	d.Colour = "Black"

	PrintColour(a)
	PrintColour(d)
}

On the playground

It will be possible to add further fields to Dog. The difference to Uriel's Answer is that calls to PrintColour will fail at compile time if something else than a struct implementing Animalizer is passed in.

Also you won't have to use a typeswitch since the compiler knows an Animalizer is implementing GetColour.

And, finally, the behaviour (printing) is separated from the struct, GetColour just returns the colour.

答案2

得分: 4

如果你在Animal类型上声明了PrintColour方法,当你在Dog中包含Animal时,它将被“继承”。

这在Go语言中被称为“嵌入”。详细信息请参阅Effective Go的“嵌入”部分

可以尝试以下代码:

package main

import "log"

type Animal struct {
    Colour string
    Name   string
}

type Dog struct {
    Animal
}

func (a *Animal) PrintColour() {
    log.Printf("%s\n", a.Colour)
}

func main() {
    a := new(Animal)
    a.Colour = "Void"
    d := new(Dog)
    d.Colour = "Black"

    a.PrintColour()
    d.PrintColour()
}

输出结果为:

2009/11/10 23:00:00 Void
2009/11/10 23:00:00 Black

Playground

英文:

If you declare the PrintColour method on the Animal type, it will be "inherited" when you include Animal in Dog.

This is known as "Embedding" in the Go world. See The "Embedding" section of Effective Go for more info.

Try something like:

package main

import "log"

type Animal struct {
    Colour string
    Name string
}

type Dog struct {
    Animal
}

func (a *Animal) PrintColour() {
    log.Printf("%s\n", a.Colour)
}


func main () {
    a := new (Animal)
    a.Colour = "Void"
    d := new (Dog)
    d.Colour = "Black"

    a.PrintColour()
    d.PrintColour()
}

Produces:

>2009/11/10 23:00:00 Void
2009/11/10 23:00:00 Black

Playground

答案3

得分: 3

你可以尝试使用interface{}来实现。

package main

import (
	"fmt"
	"reflect"
)

type Animal struct {
	Colour string
	Name   string
}

type Dog struct {
	Animal
}

func PrintColour(a interface{}) {
	switch a.(type) {
	case *Dog:
		fmt.Printf("狗的颜色:%s\n", a.(*Dog).Colour)
	case *Animal:
		fmt.Printf("动物的颜色:%s\n", a.(*Animal).Colour)
	default:
		fmt.Printf("嗯,%s\n", reflect.TypeOf(a))
	}
}

func main() {
	a := new(Animal)
	a.Colour = "无色"
	d := new(Dog)
	d.Colour = "黑色"

	PrintColour(a)
	PrintColour(d)
}

希望对你有帮助!

英文:

You could try it with interface{}

package main

import ("fmt"
	   "reflect")

type Animal struct {
    Colour string
    Name string
}

type Dog struct {
    Animal
}

func PrintColour(a interface{}) {
	switch a.(type){
		case *Dog:
			fmt.Printf("Dog %s\n", a.(*Dog).Colour)
		case *Animal:
    		fmt.Printf("Aimal %s\n", a.(*Animal).Colour)
		default:		
    		fmt.Printf("hmm %s\n", reflect.TypeOf(a))
			
	}
}


func main () {
    a := new (Animal)
    a.Colour = "Void"
    d := new (Dog)
    d.Colour = "Black"

    PrintColour(a)
    PrintColour(d)

}

答案4

得分: 1

嵌入(匿名)字段仍然可以通过使用其类型名称来显式访问:

package main

import "log"

type Animal struct {
	Colour string
	Name   string
}

type Dog struct {
	Animal
}

func PrintColour(a *Animal) {
	log.Printf("%s\n", a.Colour)
}

func main() {
	a := new(Animal)
	a.Colour = "Void"
	PrintColour(a)

	d := new(Dog)
	d.Colour = "Black"
	// 你可以通过"d.Animal"访问底层的"Animal"
	PrintColour(&d.Animal)
}

在参考文档中:第二个代码块之后的句子解释了如何声明一个“匿名”字段,并且说明:

未限定的类型名称充当字段名。

英文:

The embedded (anonymous) field can still be explicitly accessed by using its typename :

package main

import "log"

type Animal struct {
	Colour string
	Name   string
}

type Dog struct {
	Animal
}

func PrintColour(a *Animal) {
	log.Printf("%s\n", a.Colour)
}

func main() {
	a := new(Animal)
	a.Colour = "Void"
	PrintColour(a)

	d := new(Dog)
	d.Colour = "Black"
	// you can access the underlying "Animal" through "d.Animal"
	PrintColour(&d.Animal)
}

playground

In the reference : the sentence after the second code block explains how you can declare an "anonymous" field, and states :
> The unqualified type name acts as the field name.

答案5

得分: 1

我的例子可能不是很好,但你可以用这种方式做你想做的事情:

http://play.golang.org/p/JoAlOvJthr

基本上,使用接口来定义你想要对外界公开的所有类型和嵌入类型的共同功能。

(我的例子可能不是最好的,但它可以工作)

英文:

My example might not be great but you can do what you want this way:

http://play.golang.org/p/JoAlOvJthr

Essentially use an interface to define the common functionality you want to expose to the outside world for all your types and embedded types.

(My example may not be the best but it works)

huangapple
  • 本文由 发表于 2014年2月3日 20:29:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/21527489.html
匿名

发表评论

匿名网友

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

确定