Go嵌入结构调用子方法而不是父方法
Go embedded struct call child method instead parent method
这里是一个带有接口、父结构和 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
我的问题是 Shape.PrintArea
的调用,它调用了 Circle 和 Rectangle 的 Shape.Area
方法,而不是调用 Circle.Area
和 Rectangle.Area
方法。
这是 Go 中的错误吗?
感谢您的帮助。
实际上在您的示例中调用 ShapeInterface.PrintArea()
在 Circle
的情况下工作得很好,因为您为类型 Circle
创建了一个 PrintArea()
方法。由于您没有为 Rectangle
类型创建 PrintArea()
,因此将调用嵌入 Shape
类型的方法。
这不是错误,这是预期的工作方式。围棋是 not (quite) an object oriented language: it does not have classes and it does not have type inheritance; but it supports a similar construct called embedding both on struct
level and on interface
level, and it does have methods.
你期望调用的是virtual methods:你期望PrintArea()
方法会调用"overridden"Area()
方法,但是在Go中没有继承和virtual方法。
Shape.PrintArea()
的定义是调用Shape.Area()
,这就是发生的事情。 Shape
不知道它是哪个结构以及它是否嵌入其中,因此它不能 "dispatch" 方法调用到虚拟的,运行 时间方法。
Go Language Specification: Selectors 描述了评估 x.f
表达式(其中 f
可能是一个方法)以选择最终调用哪个方法时所遵循的确切规则。要点:
- A selector
f
may denote a field or method f
of a type T
, or it may refer to a field or method f
of a nested anonymous field of T
. The number of anonymous fields traversed to reach f
is called its depth in T
.
- For a value
x
of type T
or *T
where T
is not a pointer or interface type, x.f
denotes the field or method at the shallowest depth in T
where there is such an 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
是一个 Circle
并且有一个方法 Area()
和 Circle
接收者,它被调用没问题。
矩形
在 Rectangle
的情况下 si.PrintArea()
实际上会调用方法 Shape.PrintArea()
因为你 没有 定义 PrintArea()
方法对于 Rectangle
类型(没有接收者 *Rectangle
的方法)。 Shape.PrintArea()
方法的实现调用 Shape.Area()
而不是 Rectangle.Area()
- 如前所述,Shape
不知道 Rectangle
.所以你会看到
Rectangle1 : Area 0
打印而不是预期的 Rectangle1 : Area 20
。
但是如果你调用 PrintArea(si)
(传递 Rectangle
),它会调用 si.Area()
,这将是 Rectangle.Area()
,因为存在这样的方法。
这里是一个带有接口、父结构和 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
我的问题是 Shape.PrintArea
的调用,它调用了 Circle 和 Rectangle 的 Shape.Area
方法,而不是调用 Circle.Area
和 Rectangle.Area
方法。
这是 Go 中的错误吗?
感谢您的帮助。
实际上在您的示例中调用 ShapeInterface.PrintArea()
在 Circle
的情况下工作得很好,因为您为类型 Circle
创建了一个 PrintArea()
方法。由于您没有为 Rectangle
类型创建 PrintArea()
,因此将调用嵌入 Shape
类型的方法。
这不是错误,这是预期的工作方式。围棋是 not (quite) an object oriented language: it does not have classes and it does not have type inheritance; but it supports a similar construct called embedding both on struct
level and on interface
level, and it does have methods.
你期望调用的是virtual methods:你期望PrintArea()
方法会调用"overridden"Area()
方法,但是在Go中没有继承和virtual方法。
Shape.PrintArea()
的定义是调用Shape.Area()
,这就是发生的事情。 Shape
不知道它是哪个结构以及它是否嵌入其中,因此它不能 "dispatch" 方法调用到虚拟的,运行 时间方法。
Go Language Specification: Selectors 描述了评估 x.f
表达式(其中 f
可能是一个方法)以选择最终调用哪个方法时所遵循的确切规则。要点:
- A selector
f
may denote a field or methodf
of a typeT
, or it may refer to a field or methodf
of a nested anonymous field ofT
. The number of anonymous fields traversed to reachf
is called its depth inT
.- For a value
x
of typeT
or*T
whereT
is not a pointer or interface type,x.f
denotes the field or method at the shallowest depth inT
where there is such anf
.
进入细节
圆形
在 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
是一个 Circle
并且有一个方法 Area()
和 Circle
接收者,它被调用没问题。
矩形
在 Rectangle
的情况下 si.PrintArea()
实际上会调用方法 Shape.PrintArea()
因为你 没有 定义 PrintArea()
方法对于 Rectangle
类型(没有接收者 *Rectangle
的方法)。 Shape.PrintArea()
方法的实现调用 Shape.Area()
而不是 Rectangle.Area()
- 如前所述,Shape
不知道 Rectangle
.所以你会看到
Rectangle1 : Area 0
打印而不是预期的 Rectangle1 : Area 20
。
但是如果你调用 PrintArea(si)
(传递 Rectangle
),它会调用 si.Area()
,这将是 Rectangle.Area()
,因为存在这样的方法。