我打算将我们的服务器从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
修改了数组元素,则因为接口包含了指针,所以修改了a
和b
变量。对于第二个,如果f
修改了数组元素,则a
和b
不会受到影响,因为数组中的接口包含指向这些变量副本的指针。例如,如果
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
}
}
}