Can embedded struct method have knowledge of parent/child?

后端 未结 2 881
时光取名叫无心
时光取名叫无心 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.

提交回复
热议问题