我打算将我们的服务器从c ++移植到Go,并且在处理基于派生类的对象列表时遇到问题。
我的意思是说,如果我有一个称为A的基类(或接口),并且它具有子类B和C,并且我想拥有一个通用列表,该列表可以处理类型A的列表,并且该列表是派生的。

在面向对象的语言中,我只能创建一个(指针)A列表,就是这样,我可以将对象B和C添加到列表中,因为它们的确是A类型。但是由于Go没有继承,我对此感到困惑如何有效地处理这一问题。

最佳答案

如前所述,您可以使用接口来执行此操作。您可以创建[]Intf类型的列表,其中Intf是您的类型通用的接口,然后可以将实现该接口的任何类型放入列表中。

type A struct{}
type B struct{}

type Intf interface {
  // common methods of A and B
}

func f(a Intf) {
   // Here, a is A or B
}


您必须谨慎对待副本或指针。例如:
a:=A{}
b:=B{}
x:=[]Intf{&a,&b}
f(x)

不同于:
x:=[]Intf{a,b}
f(x)

对于第一个,如果f修改了数组元素,则因为接口包含了指针,所以修改了ab变量。对于第二个,如果f修改了数组元素,则ab不会受到影响,因为数组中的接口包含指向这些变量副本的指针。

例如,如果A在接口Intf中包含采用指针接收器的方法,那么您必须使用数组中所有A实例的地址。

Go的类型系统是明确且严格的。因此,例如,您通过接口实现了多态列表,并且具有以下功能:
func f(input []Intf) {
}
func g(input Intf) {
}

假设您有以下变量:
var a []A
var b A

其中A实现Intf。然后:
g(b) // This is valid
f(a) // This is *not* valid

这是因为f获得了[]Intf,但是a[]A,而不是[]Intf。要调用f,您必须构建一个[]Intf:
fInput:=make([]Intf,0,len(a))
for _,x:=range a {
   fInput=append(fInput,x)
}
f(fInput)

因此,如果要处理多态列表,那么始终使用接口列表而不是具体类型列表是有意义的。

如果需要根据类型直接访问字段,则也可以使用类型断言:
func f(in []Intf) {
   for _,x:=range in {
      if a, ok:=x.(*A); ok {
          a.Field=1
      }
   }
}

09-25 19:21