You have some conflicting information in your post that makes it hard to follow / understand what the problem is. At the top of the post you say package app results in:
Student study
Student study
Yet in your screenshot you say package app gives you:
BoyStudent study
GirlStudent study
Would you be able to share a working repo with instructions on how to reproduce? That would make it easier to understand what’s happening.
I still don’t have enough info to know what’s going on. If you’re not able to share the source repo with instructions on how to reproduce, are you able to provide diffs at each step?
This looks to be related to the fact your methods aren’t exported. You generally can’t invoke unexported (lowercased) methods or functions from other packages. As soon as I changed the methods, and interface, to use Study instead of study it started to work as I expected.
It seems like the compiler takes in to account whether the method is exported or not when deciding which to invoke. So in short, if you’re exporting an interface outside of a package you should also export all of the methods needed to implement said interface.
Let me know if switching study to Study fixes it for you.
It looks like you’re trying to treat struct embedding like inheritance, but “overriding” one of the methods of the embedded type. Struct embedding is not inheritance, and may result in odd behaviors like this. In this case, it seems like the app.Student type shouldn’t have a Study method.
Yes, use Study can be work now, but i’m not sure it will always work in any other condition, I will always pay attention to this question
As you said, i’m trying to treat struct embedding like inheritance, since golang provides such a feature, it should be used. Even if it cannot be used as inheritance, there should be no uncertainties in computer, either all cases can be accessed, or none can be accessed, instead of being affected by the package name and producing different results
Let me know is it a issue for language or not, and how will golang handle it.
Per the things I’ve seen, it should work a little more reliably when you export the function by capitalizing it. If you unexport it, and work with an exported interface, it seems like the behavior is variable.
To your point about consistency, that isn’t always the case. Languages have undefined or unexpected behaviors all of the time. I’d argue that having an unexported method defined in an exported interface is a behavior that’s allowed but may result in undefined behaviors. It’s also a pattern indicating the code isn’t written as thoughtfully as it could.
Should it be disallowed and result in a compiler error? Maybe, that’s hard to say as some may depend on it and never hit this kind of issue.
I don’t know if I agree that struct embedding should be used because the language has it. The Go language allows you to redefine what nil means in the current scope, but that doesn’t make it a good idea:
I never use struct embedding in my programs, likely because I also don’t write my Go code like it’s an OOP language. Coming from a Ruby background I wanted things like inheritance, but I’ve learned I rarely need it.
It looks like it’s a bug. I recreated a minimal example. If you rename the folder “child” to “achild”, then Child will be printed, if folder name is “child” than just Base will be. It turns out that Go imports packages in alphabetical order.
scratch/base/base.go
import "fmt"
type Interface interface {
do()
}
type Base struct{}
func (Base) do() { fmt.Println("base") }
func Call(f func() Interface) {
f().do()
}
To ensure reproducible initialization behavior, build systems are encouraged to present multiple files belonging to the same package in lexical file name order to a compiler.