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

ter*_*975 4 methods struct parent-child go

这里是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)
    }

}
Run Code Online (Sandbox Code Playgroud)

我有结果:

$ 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
Run Code Online (Sandbox Code Playgroud)

我的问题是调用Shape.PrintArea该调用Shape.Area方法Circle和Rectangle,而不是调用Circle.AreaRectangle.Area方法.

这是Go中的一个错误吗?

谢谢你的帮助.

icz*_*cza 6

实际上在你的例子中,ShapeInterface.PrintArea()在a的情况下调用工作正常,Circle因为你PrintArea()为类型创建了一个方法Circle.由于您没有PrintArea()Rectangle类型创建,因此Shape将调用嵌入类型的方法.

这不是错误,这是预期的工作.Go 不是(完全)一种面向对象的语言:它没有类,也没有类型继承 ; 但是它支持一个类似的构造,在层次和层次上都称为嵌入,它确实有方法.structinterface

您期望的是虚拟方法:您希望该PrintArea()方法将调用"重写" Area()方法,但在Go中没有继承和虚方法.

定义Shape.PrintArea()是调用Shape.Area(),这就是发生的事情.Shape不知道它是哪个结构以及它是否嵌入,因此它不能将方法调用"调度"到虚拟的运行时方法.

围棋语言规范:选择器描述的评价时,其遵循确切规则x.f表达(其中,f可以是方法)选择哪种方法,将在结束时调用.关键点:

  • 选择器f可以表示一个字段或方法f的类型的T,或者它可以指字段或方法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())
}
Run Code Online (Sandbox Code Playgroud)

在这个方法c.Area()中调用where c是a *Circle,所以*Circle调用带接收器的方法也存在.

PrintArea(si)电话si.Area().由于si是一个Cicle,有一个方法Area()Circle接收机,它被调用没有问题.

长方形

如果Rectangle si.PrintArea()将实际调用该方法,Shape.PrintArea()因为您没有PrintArea()为该Rectangle类型定义方法(没有接收器的方法*Rectangle).和执行Shape.PrintArea()方法调用Shape.Area() 没有 Rectangle.Area() -作为讨论,Shape不知道Rectangle.所以你会看到

Rectangle1 : Area 0
Run Code Online (Sandbox Code Playgroud)

打印而不是预期的Rectangle1 : Area 20.

但是如果你调用PrintArea(si)(传递Rectangle),它会调用si.Area()哪个Rectangle.Area()因为这样的方法存在.