这里是带有接口,父结构和2个子结构的Go代码示例
package main import ( "fmt" "math" ) // Shape Interface : defines methods type ShapeInterface interface { Area() float64 GetName() string PrintArea() } // Shape Struct : standard shape with an area equal to 0.0 type Shape struct { name string } func (s *Shape) Area() float64 { return 0.0 } func (s *Shape) GetName() string { return s.name } func (s *Shape) PrintArea() { fmt.Printf("%s : Area %v\r\n", s.name, s.Area()) } // Rectangle Struct : redefine area method type Rectangle struct { Shape w, h float64 } func (r *Rectangle) Area() float64 { return r.w * r.h } // Circle Struct : redefine Area and PrintArea method type Circle struct { Shape r float64 } func (c *Circle) Area() float64 { return c.r * c.r * math.Pi } func (c *Circle) PrintArea() { fmt.Printf("%s : Area %v\r\n", c.GetName(), c.Area()) } // Genreric PrintArea with Interface func PrintArea (s ShapeInterface){ fmt.Printf("Interface => %s : Area %v\r\n", s.GetName(), s.Area()) } //Main Instruction : 3 Shapes of each type //Store them in a Slice of ShapeInterface //Print for each the area with the call of the 2 methods func main() { s := Shape{name: "Shape1"} c := Circle{Shape: Shape{name: "Circle1"}, r: 10} r := Rectangle{Shape: Shape{name: "Rectangle1"}, w: 5, h: 4} listshape := []c{&s, &c, &r} for _, si := range listshape { si.PrintArea() //!! Problem is Witch Area method is called !! PrintArea(si) } }
我有结果:
$ go run essai_interface_struct.go Shape1 : Area 0 Interface => Shape1 : Area 0 Circle1 : Area 314.1592653589793 Interface => Circle1 : Area 314.1592653589793 Rectangle1 : Area 0 Interface => Rectangle1 : Area 20
我的问题是对Circle和Rectangle调用Shape.PrintArea哪种调用Shape.Area方法,而不是调用Circle.Area和Rectangle.Area方法。
Shape.PrintArea
Shape.Area
Circle.Area
Rectangle.Area
这是Go中的错误吗?
谢谢你的帮助。
实际上,在您的示例ShapeInterface.PrintArea()中,对于a而言,调用工作正常,Circle因为您PrintArea()为该类型创建了一个方法Circle。由于您没有PrintArea()为Rectangle类型创建,因此Shape将调用嵌入式类型的方法。
ShapeInterface.PrintArea()
Circle
PrintArea()
Rectangle
Shape
这不是错误,这是预期的工作。Go 不是(相当)一种面向对象的语言:它没有类,也没有类型继承。但它支持类似的构造,称为在层和层上 嵌入 ,并且确实具有方法。struct``interface
struct``interface
您所期望的被称为 虚拟方法 :您期望该PrintArea()方法将调用“重写” Area()方法,但是在Go中没有继承和虚拟方法。
Area()
的定义Shape.PrintArea()是调用Shape.Area(),这就是发生的情况。Shape不知道它是哪个结构以及是否嵌入其中,因此它无法将方法调用“分派”到虚拟的运行时方法。
Shape.PrintArea()
Shape.Area()
的围棋语言规范:选择器描述的评价时,其遵循确切规则x.f表达(其中,f可以是方法)选择哪种方法,将在结束时调用。关键点:
x.f
f
* 选择器f可以表示一个字段或方法f的类型的T,或者它可以指字段或方法f嵌套的匿名字段的T。走过来达到匿名的字段数f被称为其 深度 在T。 * 对于值x类型的T或*T其中T不是指针或接口类型,x.f表示在最浅深度域或方法T,其中有这样一个f。
T
x
*T
在以下情况下Circle:si.PrintArea()会叫Circle.PrintArea(),因为你创造了这样的方法:
si.PrintArea()
Circle.PrintArea()
func (c *Circle) PrintArea() { fmt.Printf("%s : Area %v\r\n", c.GetName(), c.Area()) }
在此方法c.Area()中,称为where c是a *Circle,因此*Circle将调用存在接收者的方法。
c.Area()
c
*Circle
PrintArea(si)来电si.Area()。由于si是,Circle并且有一个Area()带有Circle接收方的方法,因此调用它没有问题。
PrintArea(si)
si.Area()
si
在情况下,Rectangle si.PrintArea()实际上会调用该方法,Shape.PrintArea()因为您 没有PrintArea()为该Rectangle类型定义一个方法(receive方法没有任何方法*Rectangle)。和执行Shape.PrintArea()方法调用Shape.Area() 没有 Rectangle.Area() -作为讨论,Shape不知道Rectangle。所以你会看到
*Rectangle
Rectangle.Area()
Rectangle1 : Area 0
印刷而不是预期Rectangle1 : Area 20。
Rectangle1 : Area 20
但是,如果您调用PrintArea(si)(传递了Rectangle),则会调用si.Area(),这是Rectangle.Area()因为该方法存在。