dongtan3395
dongtan3395
2016-05-05 20:10

在嵌入式功能中访问功能

已采纳

First, I'm quite new to Go, so please forgive any lapses or errors in terminology. I suspect my lack of command with the terminology is also partially to blame for not finding an answer to the following question after many hours of looking.

In short, I would like the output of the following code to be

I am the Adult
I am the Child

Where instead the output is

I am the Adult
I am the Adult

Code:

package main

import "fmt"

type Human struct {
    age  uint
    name string
}

func (h Human) sayName() error {
    fmt.Println("I am the Adult")
    return nil
}

func (h Human) Introduce() error {
    h.sayName()
    return nil
}

type Child struct {
    Human
}

func (c Child) sayName() error {
    fmt.Println("I am the Child")
    return nil
}

func main() {
    h := Human{}
    h.Introduce()

    c := Child{Human{}}
    c.Introduce()
}

So in essence, though Introduce() is only implemented in the embedded type, Human, it calls sayName(), which is implemented in both the embedded type and the embedding type.

I understand that the current output is the way as it is because the embedded Human struct doesn't "know" it's embedded and thus could never call Child.sayName and would only call its own sayName() function.

Is there a way to instantiate a Human struct (or a struct embedding one) in which you can "replace" Human.sayName() with an alternate sayName() function?

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

3条回答

  • dtfo55908 dtfo55908 5年前

    The way to get this kind of late-bound behavior is to use interfaces. If Introduce was a method on an interface that required a sayName method, then Human and Child would both satisfy that interface, and could Introduce themselves, and the proper sayName method would be called in either case, because it would be dispatched via the interface type, instead of via Human.

    点赞 评论 复制链接分享
  • doufang1954 doufang1954 5年前

    I was thinking about it incorrectly. as hobbs and evanmcdonnal said, the "correct" way to do this is to make the Introduce() function a function with an interface argument and then to create an interface which both Child and Human implement.

    The following gives the desired output:

    package main
    
    import "fmt"
    
    type Talker interface {
        sayName() error
    }
    
    type Human struct {
        age  uint
        name string
    }
    
    func (h Human) sayName() error {
        fmt.Println("I am the Adult")
        return nil
    }
    
    type Child struct {
        Human
    }
    
    func Introduce(t Talker) error {
        t.sayName()
        return nil
    }
    
    func (c Child) sayName() error {
        fmt.Println("I am the Child")
        return nil
    }
    
    func main() {
        h := Human{}
        Introduce(h)
    
        c := Child{Human{}}
        Introduce(c)
    }
    
    点赞 评论 复制链接分享
  • dongnue2071 dongnue2071 5年前

    What you would like to do is not possible. As you stated, the Human type could never know that it's embedded in the Child type and therefor when Introduce is called it has no way of invoking the Child types version of sayName(). You're trying to invert the relationship, to have an embedded type call out to another version of it's method in the embeddor.

    Methods for an embedded type are hoisted to the scope of the embeddor but that is a one way street, which is sensible. In this situation you would just need to structure your code differently. Most likely, whatever behavior you're looking for can be achieved with the use of interfaces rather than embedding. I would provide more of an example but with your code sample there isn't much to say, obviously to solve the problem here you would just call sayName() and would never define Introduce in the first place since it does nothing.

    It's possible you should just be embedding in the opposite direction. Human contains no reference to Child but Child does have a reference to Human and, in a case like this, where both have a definition for sayName(), Child could call Human's by doing c.Human.sayName(). So my intuition says you've embedded in the wrong direction or you should use interfaces. This is purely an assumption, but I would guess you might have the wrong idea since you're referring to Human as the and 'adult' (like the parent) and Child as child (like a child class). This isn't the case at all. Embedding is the same as composition only the embedded types methods and fields are hoisted or lifted or whatever term you like to say they're moved up to the scope of the embeddor. That being said, I see a lot of confusion around this on SO and it's almost always because the asker is looking at this like it's inheritance when really it's composition.

    点赞 评论 复制链接分享

相关推荐