Can embedded struct method have knowledge of paren

2019-01-08 02:15发布

问题:

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 PHP, I had a hard time grasping composition and embedding in Go. I think it finally clicked recently and want to use it more, but I have hit a roadblock for one idea I have.

Lets say I have a struct from a package that has a Validate method and I embedded that in my struct in the package I am currently working in. Is there a way (I own both packages) that I can get a reference to the struct that is doing the embedding inside of the Validate method? Kind of like a way to see who called me since I would be calling it on the "parent" instead of the embedded.

I'll try to visualize this...

type B struct {}
func (b B) Validate() {
  // get reference somehow of A obj
}


type A struct {
  B
}

a := A{B{}}
a.Validate()

I feel as though this isn't possible, but it would be cool if you could. I'd imagine that you would need to make A and B both the same interface type for this to work. Any thoughts or help will be much appreciated.

回答1:

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.



回答2:

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.