这里是Go代码示例,包含Interface,Parent Struct和2 Children Structs
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
我的问题是调用了Shape.PrintArea
调用圆{1}}方法而不是调用Shape.Area
和Circle.Area
方法。
这是Go中的错误吗?
感谢您的帮助。
答案 0 :(得分:6)
实际上,在您的示例中,ShapeInterface.PrintArea()
调用Circle
可以正常工作,因为您为类型PrintArea()
创建了Circle
方法。由于您没有为PrintArea()
类型创建Rectangle
,因此将调用嵌入式Shape
类型的方法。
这不是错误,这是预期的工作。 Go是not (quite) an object oriented language:它没有类,does not have type inheritance;但是它在struct
级别和interface
级别上支持一个名为 embedding 的类似构造,它确实有methods。
您期望的是virtual methods:您希望PrintArea()
方法将调用"覆盖" Area()
方法,但在Go中没有继承和虚方法。
Shape.PrintArea()
的定义是调用Shape.Area()
,这就是发生的事情。 Shape
不知道它是哪个结构以及它是否嵌入,所以它不能发送"发送"方法调用虚拟的运行时方法。
Go Language Specification: Selectors描述了在评估x.f
表达式(其中f
可能是一种方法)时选择的确切规则,以选择最终调用哪种方法。要点:
- 选择器
f
可以表示类型f
的字段或方法T
,也可以指嵌套{{3}的字段或方法f
}T
。遍历到f
的匿名字段数在T
中称为深度。- 对于类型
x
或T
的值*T
,T
不是指针或接口类型,x.f
表示字段或方法T
中最浅的深度,其中有f
。
如果Circle
:si.PrintArea()
会调用Circle.PrintArea()
,因为您创建了这样的方法:
func (c *Circle) PrintArea() {
fmt.Printf("%s : Area %v\r\n", c.GetName(), c.Area())
}
在此方法中调用c.Area()
,其中c
是*Circle
,因此将调用带有*Circle
接收器的方法,该方法也存在。
PrintArea(si)
来电si.Area()
。由于si
是Cicle
,而且Area()
方法带有Circle
接收方,因此调用它没问题。
如果Rectangle
si.PrintArea()
实际上会调用方法Shape.PrintArea()
,因为没有为{{1}定义PrintArea()
方法类型(没有接收器Rectangle
的方法)。 *Rectangle
方法的实施调用Shape.PrintArea()
不 Shape.Area()
- 正如所讨论的那样,Rectangle.Area()
并不了解Shape
。所以你会看到
Rectangle
打印而不是预期的Rectangle1 : Area 0
。
但是,如果您致电Rectangle1 : Area 20
(通过PrintArea(si)
),则会调用Rectangle
si.Area()
,因为此方法存在。