Can embedded struct method have knowledge of parent/child?

后端 未结 2 863
时光取名叫无心
时光取名叫无心 2020-11-28 16:56

I have been working with Go on and off on my free time for a few months and I feel I\'ve been getting a hang of it. Coming from traditional OOP languages, such as Java and

相关标签:
2条回答
  • 2020-11-28 17:36

    This is something not possible. To better understand why, see this slightly modified version of your example:

    type B struct{}
    
    func (b *B) Validate() {
        fmt.Printf("b:%p\n", b)
    }
    
    type A struct {
        *B
    }
    
    func main() {
        b := B{}
        a1 := A{&b}
        a2 := A{&b}
    
        a1.Validate()
        a2.Validate()
        a1.B.Validate()
        a2.B.Validate()
    
        b.Validate()
    }
    

    Output as expected, the same value of *B everywhere (Go Playground):

    b: 0x1becd8
    b: 0x1becd8
    b: 0x1becd8
    b: 0x1becd8
    b: 0x1becd8
    

    As you can see, I changed the method receiver and the embedded type to pointer of B.

    From this example it is clear: you can use the same value of B (or rather the same address of a value of B) to embed in different values of type A!

    And you can call Validate() on both: so the theoretical "parent" is not even "constant" for a given value of *B. This wouldn't be a deal-breaker, but: having a value of A, you can call the promoted method B.Validate() by writing a.Validate() which is OK, but you can also call it like a.B.Validate() - now this time you don't really have a value of A (arguable, but Validate() is called on a value of *B and not A), but finally you can also call b.Validate() - this time you definitely don't have a value of A.

    There is no definite type for the parent, B (or *B) can be embedded in any type (so it couldn't be anything other than interface{}).

    So image: you have a concrete value of *B and when its Validate() method is called, sometimes there is a parent, and sometimes there isn't. Then what would be the justification to have a parent at all?

    Back to your example: for this Validate() method to validate something meaningful, it should (must) be passed to the Validate() method as a parameter - explicitly and not automatically.

    The best you can do is what captncraig wrote in his answer (+1).

    You can simplify it a little though if you explicitly add a method to A which would call B.Validate() like this:

    func (a *A) Validate2() {
        a.Validate(a)
    }
    
    // And using it:
    a.Validate2()
    

    There is no justification for a validator of the kind you imagine to be embedded in A, it should just be a field of A, or since there is no direct relation, it can be an "independent" validator. In both of these cases you may add a helper Validate() method to A if you would like to simplify validation.

    0 讨论(0)
  • 2020-11-28 17:36

    Any kind of parent reference is impossible. What would you hope to do with the parent? Since they would be potentially widely different types, they would be incompatible to do anything meaningful with.

    If you need something in common between different types you want an interface, and you will need to pass it in:

    type B struct {}
    func (b B) Validate(parent MyInterface) {
    }
    
    
    type A struct {
      B
    }
    // A implements MyInterface
    a := A{B{}}
    a.Validate(a)
    

    If you can explain what you are actually trying to accomplish there is almost certainly a better way than this.

    0 讨论(0)
提交回复
热议问题