我正在努力学习Go,但我不断地抨击它的一些与其他语言不同的概念。
假设我有一个结构
type Vehicle struct {
Seats int
}现在我想要另一个结构,它嵌入了Vehicle
type Car struct {
Vehicle
Color string
}据我所知,Car结构现在嵌入了Vehicle。
现在我想要一个函数,它可以使用任何车辆
func getSeats(v Vehicle){
return v.Seats
}但是每当我试图传递一个Car
getSeats(myCar)我得到以下错误:
cannot use myCar (value of type Car) as Vehicle value in argument to getSeats
但是我的IDE告诉我myCar有一个Seats属性!
由此,我了解到嵌入与继承是不一样的。
我的问题是:是否存在类似于结构继承(如c++ )的结构继承,其中一个函数可以接受一个基本结构,或者这是否与Go处理完全不同?我将如何在“前进之路”中实现这样的东西?
发布于 2022-01-15 11:38:31
就像你提到的,Go没有典型意义上的继承。嵌入实际上只是语法上的糖。
嵌入时,可以向结构中添加一个字段,其名称与嵌入类型完全相同。嵌入结构的任何方法都可以在嵌入它们的结构上调用,这只不过是转发它们而已。
一个问题是,如果嵌入另一个方法的结构已经声明了一个方法,那么它将被优先于转发它,如果您想这样想的话,它允许您覆盖一些函数。
正如您已经注意到的,即使Car嵌入了Vehicle,我们也不能将它们作为Vehicle使用,因为它们完全不是相同的类型。但是嵌入Vehicle的任何结构都将包含由Vehicle定义的所有方法,因此如果我们定义了Vehicle实现的接口,那么嵌入Vehicle的所有类型也应该实现该接口。
例如:
package main
import (
"fmt"
)
type Seater interface {
Seats() int
}
type Vehicle struct {
seats int
}
func (v *Vehicle) Seats() int {
return v.seats
}
type Car struct {
Vehicle
Color string
}
type Bike struct {
Vehicle
Flag bool
}
// A bike always has 1 seat
func (b *Bike) Seats() int {
return 1
}
type Motorcycle struct {
Vehicle
Sidecar bool
}
// A motorcycle has the base amounts of seats, +1 if it has a side car
func (m *Motorcycle) Seats() int {
return m.Vehicle.seats + 1
}
func getSeats(v Seater) int {
return v.Seats()
}
func main() {
fmt.Println(getSeats(&Bike{
Vehicle: Vehicle{
seats: 2, // Set to 2 in the Vehicle
},
Flag: true,
}))
fmt.Println(getSeats(&Motorcycle{
Vehicle: Vehicle{
seats: 1,
},
Sidecar: true,
}))
fmt.Println(getSeats(&Car{
Vehicle: Vehicle{
seats: 4,
},
Color: "blue",
}))
}这些指纹:
1
2
4在Bike的情况下,调用Bike.Seats方法,这就是为什么它返回1,即使它的Vehicle的seats值是2。
在Motorcycle的情况下,Motorcycle.Seats方法也被调用,但是在这里我们可以访问嵌入式类型并仍然使用它来获得结果。
在Car的情况下,调用Vehicle.Seats方法是因为Car没有“覆盖”Seats。
发布于 2022-01-15 12:12:13
这是我一开始就关心的事情。我认为使用像Java这样的OOP语言的概念是自然的方式。当然,Go不是面向对象的,也不是通过继承来实现多态性的。最后,支持的组合从继承提供的东西中获取好的东西,并删除那些增加了更多开销的东西,然后是有效的帮助。Go对多态性的回答是接口。Go试着变得简单,而你通常只能用一种显而易见的方式来完成事情。假设您有这样的Java类:
class Base {
private int attribute;
public int getAttribute() {
return this.attribute;
}
}
class Something extends Base {}在java中,每个对象都在堆上,并且您有指向它的指针。它可以是null,这也意味着当您传递它时,它的大小是相同的。这是您可以更自由地传递对象的原因之一(无论您传递Base还是Something,只要参数类型是超类,它就会编译)。Go的设计是为了更有效地管理内存,比堆更多地使用堆栈,甚至堆也不那么支离破碎。当您声明一个struct时,该结构的大小取决于它所保存的数据,因此您不能将它传递到嵌入式结构所属的位置。让我们举一个例子:
type Base struct {
attribute int32
}
func (b *Base) Attribute() int32 {
return b.attribute
}
type Something struct {
garbage int32
Base
}而Base有4个字节,Something有8个字节,attribute有不同的偏移量。如果编译器允许您以Something代替Base,那么您将访问garbage而不是attribute。如果您想要得到这种行为,您必须使用接口。幸运的是,您需要声明的是:
type BaseInterface interface {
Attribute() int32
}既然您有了这个接口,就可以对嵌入Base的所有结构(除非它还嵌入了具有相同级别的方法名Attribute的其他东西)进行通用函数,如下所示:
func DoubleOfBaseAttribute(base BaseInterface) int32 {
return base.Attribute() * 2
}这也被称为动态调度( Dynamic ),以及C++或Java等语言隐式使用的内容。
https://stackoverflow.com/questions/70720911
复制相似问题