去嵌入结构调用子方法而不是父方法 [英] Go embedded struct call child method instead parent method

查看:99
本文介绍了去嵌入结构调用子方法而不是父方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这里带有一个接口,一个Parent Struct和两个子结构的Go代码示例

  package main 

导入(
fmt
数学


// Shape接口:定义方法
类型ShapeInterface接口{
Area()float64
GetName()字符串
PrintArea()
}

//形状结构:面积等于0.0的标准形状
类型结构{
名称字符串
}

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\\\
,s.name,s.Area())
}

//矩形Struct:重新定义区域方法
类型Rectangle struct {
Shape
w,h float64
}

func(r * Rectangle)Area()float64 {
return rw * rh
}

//圆形结构:重新定义区域和打印区域m ethod
类型Circle结构{
Shape
r float64
}

func(c * Circle)Area()float64 {
return cr * cr * math.Pi
}

func(c * Circle)PrintArea(){
fmt.Printf(%s:Area%v \r\\\
,c.GetName(),c.Area())
}

//带接口
的Genreric PrintArea func PrintArea(s ShapeInterface){
fmt.Printf (Interface => %s:Area%v \r \\\
,s.GetName(),s.Area())
}

//主指令:3种每种类型
//将它们存储在一个ShapeInterface Slice中
//通过调用2个方法为每个区域打印
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:=范围列表{
si.PrintArea()// !!问题是Witch Area方法被调用!!
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调用 Shape.Area 方法Rectangle.Area 方法。

这是Go的一个bug吗?



感谢您的帮助。

实际上,在你的示例中调用 ShapeInterface.PrintArea() Circle 因为您为 Circle 类型创建了 PrintArea()方法。由于您没有为 Rectangle 类型创建 PrintArea(),嵌入的 Shape 类型将被调用。



这不是一个错误,这是预期的工作。 Go是不是(完全)面向对象的语言:它没有类,它< a href =http://golang.org/doc/faq#inheritance =nofollow>没有类型继承;但它支持 struct 级别和接口级别上类似的嵌入结构,它有



你期望的是 虚拟方法 :你期望 PrintArea()方法将调用重载 Area()方法,但是在Go中没有继承和虚方法。
$ b

Shape.PrintArea()的定义是调用 Shape.Area() 这就是发生的情况。 Shape 不知道它是哪个结构,并且它是否嵌入,所以它不能调度方法调用到虚拟的运行时方法。 / p>

语言规范:选择器描述在评估 xf 表达式(其中 f 可能是一种方法)时选择哪种方法的确切规则最终被调用。要点:$ b​​
$ b



  • 选择符 f 可能表示 T 类型的字段或方法 f ,或者它可能引用字段或方法 的嵌套匿名字段的> f Ť。遍历到 f 的匿名字段的数量在 T 中称为它的 depth

  • 对于 T 类型的值 x * T 其中 T 不是指针或接口类型, xf 表示字段或方法在 T 中的最浅深度处存在这样的 f




细节描述



圈子



如果 Circle si.PrintArea()将会调用 Circle .PrintArea()因为你创建了这样的一个方法:

  func(c * Circle)PrintArea ){
fmt.Printf(%s:Area%v \r\\\
,c.GetName(),c.Area())
}
>

在这个方法中, > c 是一个 * Circle ,所以带有 * Circle 接收器的方法将被调用这也存在。



PrintArea(si)调用 si.Area()。由于 si 是一个 Cicle 并且有一个方法 Area() Circle 接收器,它被调用没有问题。

矩形



如果 Rectangle si.PrintArea()实际上会调用方法<$ c $因为没有为<$ c $>定义 PrintArea()方法,所以 Shape.PrintArea() c> Rectangle type(接收方 * Rectangle 没有方法)。并且 Shape.PrintArea()方法的实现调用 Shape.Area() not Rectangle.Area() - 如上所述, Shape 不知道 Rectangle 。所以你会看到

  Rectangle1:Area 0 

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



但是如果你调用 PrintArea(si)(传递 Rectangle ),它会调用 si。 Area()这将是 Rectangle.Area(),因为这种方法存在。


Here a sample of Go code with an Interface, a Parent Struct and 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)
    }

}

I have for results :

$ 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

My problem is the call of Shape.PrintArea which call Shape.Area method for Circle and Rectangle instead calling Circle.Area and Rectangle.Area method.

Is this a bug in Go ?

Thanks for your help.

解决方案

Actually in your example calling ShapeInterface.PrintArea() works just fine in case of a Circle because you created a PrintArea() method for the type Circle. Since you did not create a PrintArea() for the Rectangle type, the method of the embedded Shape type will be called.

This is not a bug, this is the intended working. Go is 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.

What you expect is called virtual methods: you expect that the PrintArea() method will call the "overridden" Area() method, but in Go there is no inheritance and virtual methods.

The definition of Shape.PrintArea() is to call Shape.Area() and this is what happens. Shape does not know about which struct it is and if it is embedded in, so it can't "dispatch" the method call to a virtual, run-time method.

The Go Language Specification: Selectors describe the exact rules which are followed when evaluating an x.f expression (where f may be a method) to choose which method will be called in the end. Key Points:

  • 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.

Going into details

Circle

In case of Circle: si.PrintArea() will call Circle.PrintArea() because you created such a method:

func (c *Circle) PrintArea() {
    fmt.Printf("%s : Area %v\r\n", c.GetName(), c.Area())
}

In this method c.Area() is called where c is a *Circle, so the method with *Circle receiver will be called which also exists.

PrintArea(si) calls si.Area(). Since si is a Cicle and there is a method Area() with Circle receiver, it is invoked no problem.

Rectangle

In case of Rectangle si.PrintArea() will actually call the method Shape.PrintArea() because you did not define a PrintArea() method for the Rectangle type (there is no method with receiver *Rectangle). And the implementation of Shape.PrintArea() method calls Shape.Area() not Rectangle.Area() - as discussed, Shape doesn't know about Rectangle. So you'll see

Rectangle1 : Area 0

printed instead of the expected Rectangle1 : Area 20.

But if you call PrintArea(si) (passing the Rectangle), it calls si.Area() which will be Rectangle.Area() because such method exists.

这篇关于去嵌入结构调用子方法而不是父方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆