转换嵌入式struct call子方法代替父方法

时间:2015-04-01 12:22:06

标签: methods struct go parent-child

这里是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.AreaCircle.Area方法。

这是Go中的错误吗?

感谢您的帮助。

1 个答案:

答案 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中称为深度
  •   
  • 对于类型xT的值*TT不是指针或接口类型,x.f表示字段或方法T中最浅的深度,其中有f
  •   

进入细节

如果Circlesi.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()。由于siCicle,而且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(),因为此方法存在。