为什么Go语言允许我调用未实现的方法?

huangapple go评论119阅读模式
英文:

Why does Go allow me to call methods that are not implemented?

问题

Go似乎没有强制要求结构体遵循接口。为什么以下代码能够编译通过?

  1. package main
  2. type LocalInterface interface {
  3. SomeMethod(string) error
  4. SomeOtherMethod(string) error
  5. }
  6. type LocalStruct struct {
  7. LocalInterface
  8. myOwnField string
  9. }
  10. func main() {
  11. var localInterface LocalInterface = &LocalStruct{myOwnField:"test"}
  12. localInterface.SomeMethod("calling some method")
  13. }

看起来这段代码不应该编译通过,因为SomeMethod没有被实现。但是使用go build命令并没有报错。

运行代码会导致运行时错误:

  1. > go run main.go
  2. panic: runtime error: invalid memory address or nil pointer dereference
  3. [signal 0xc0000005 code=0x0 addr=0x20 pc=0x4013b0]
  4. goroutine 1 [running]:
  5. panic(0x460760, 0xc08200a090)
  6. C:/Go/src/runtime/panic.go:464 +0x3f4
  7. main.(*LocalStruct).SomeMethod(0xc0820064e0, 0x47bf30, 0x13, 0x0, 0x0)
  8. <autogenerated>:3 +0x70
  9. main.main()
  10. C:/Users/kdeenanauth/Documents/git/go/src/gitlab.com/kdeenanauth/structTest/main.go:16 +0x98
  11. exit status 2
英文:

Go doesn't seem to enforce the struct adhering to the interface. Why does the following code compile?
<!-- language: go -->

  1. package main
  2. type LocalInterface interface {
  3. SomeMethod(string) error
  4. SomeOtherMethod(string) error
  5. }
  6. type LocalStruct struct {
  7. LocalInterface
  8. myOwnField string
  9. }
  10. func main() {
  11. var localInterface LocalInterface = &amp;LocalStruct{myOwnField:&quot;test&quot;}
  12. localInterface.SomeMethod(&quot;calling some method&quot;)
  13. }

It seems like this should not compile since SomeMethod is not implemented.go build results in no issues.

Running it results in the runtime error:

  1. &gt; go run main.go
  2. panic: runtime error: invalid memory address or nil pointer dereference
  3. [signal 0xc0000005 code=0x0 addr=0x20 pc=0x4013b0]
  4. goroutine 1 [running]:
  5. panic(0x460760, 0xc08200a090)
  6. C:/Go/src/runtime/panic.go:464 +0x3f4
  7. main.(*LocalStruct).SomeMethod(0xc0820064e0, 0x47bf30, 0x13, 0x0, 0x0)
  8. &lt;autogenerated&gt;:3 +0x70
  9. main.main()
  10. C:/Users/kdeenanauth/Documents/git/go/src/gitlab.com/kdeenanauth/structTest/main.go:16 +0x98
  11. exit status 2

答案1

得分: 7

当一个类型被嵌入(例如,在你的例子中,LocalInterface 被嵌入在 LocalStruct 中),Go 会创建一个嵌入类型的字段,并将其方法提升到包含它的类型中。

所以以下声明:

  1. type LocalStruct struct {
  2. LocalInterface
  3. myOwnField string
  4. }

等同于

  1. type LocalStruct struct {
  2. LocalInterface LocalInterface
  3. myOwnField string
  4. }
  5. func (ls *LocalStruct) SomeMethod(s string) error {
  6. return ls.LocalInterface.SomeMethod(s)
  7. }

你的程序因为 LocalInterface 字段是 nil 而导致空指针解引用的恐慌。

以下程序“修复”了这个恐慌(http://play.golang.org/p/Oc3Mfn6LaL):

  1. package main
  2. type LocalInterface interface {
  3. SomeMethod(string) error
  4. }
  5. type LocalStruct struct {
  6. LocalInterface
  7. myOwnField string
  8. }
  9. type A int
  10. func (a A) SomeMethod(s string) error {
  11. println(s)
  12. return nil
  13. }
  14. func main() {
  15. var localInterface LocalInterface = &LocalStruct{
  16. LocalInterface: A(10),
  17. myOwnField: "test",
  18. }
  19. localInterface.SomeMethod("calling some method")
  20. }
英文:

When a type is embedded (in your example LocalInterface is embedded inside LocalStruct), Go creates a field of the embedded type and promotes its methods to the enclosing type.

So the following declaration

  1. type LocalStruct struct {
  2. LocalInterface
  3. myOwnField string
  4. }

is equivalent to

  1. type LocalStruct struct {
  2. LocalInterface LocalInterface
  3. myOwnField string
  4. }
  5. func (ls *LocalStruct) SomeMethod(s string) error {
  6. return ls.LocalInterface.SomeMethod(s)
  7. }

Your program panics with nil pointer dereference because LocalInterface field is nil.

The following program "fixes" the panic (http://play.golang.org/p/Oc3Mfn6LaL):

  1. package main
  2. type LocalInterface interface {
  3. SomeMethod(string) error
  4. }
  5. type LocalStruct struct {
  6. LocalInterface
  7. myOwnField string
  8. }
  9. type A int
  10. func (a A) SomeMethod(s string) error {
  11. println(s)
  12. return nil
  13. }
  14. func main() {
  15. var localInterface LocalInterface = &amp;LocalStruct{
  16. LocalInterface: A(10),
  17. myOwnField: &quot;test&quot;,
  18. }
  19. localInterface.SomeMethod(&quot;calling some method&quot;)
  20. }

答案2

得分: 1

进一步调查后,我发现避免嵌入可以得到适当的错误处理。在我的情况下,这将是首选方法:

  1. package main
  2. type LocalInterface interface {
  3. SomeMethod(string) error
  4. SomeOtherMethod(string) error
  5. }
  6. type LocalStruct struct {
  7. myOwnField string
  8. }
  9. func main() {
  10. var localInterface LocalInterface = &LocalStruct{myOwnField:"test"}
  11. localInterface.SomeMethod("calling some method")
  12. }

结果为:

  1. .\main.go:13: 无法将 LocalStruct 字面量(类型 *LocalStruct)分配给 LocalInterface 类型:
  2. *LocalStruct 未实现 LocalInterface(缺少 SomeMethod 方法)
英文:

Upon further investigation I found that avoiding embedding gets the proper error handling treatment. That would be preferred in my case:
<!-- language: go -->

  1. package main
  2. type LocalInterface interface {
  3. SomeMethod(string) error
  4. SomeOtherMethod(string) error
  5. }
  6. type LocalStruct struct {
  7. myOwnField string
  8. }
  9. func main() {
  10. var localInterface LocalInterface = &amp;LocalStruct{myOwnField:&quot;test&quot;}
  11. localInterface.SomeMethod(&quot;calling some method&quot;)
  12. }

Results in:
>.\main.go:13: cannot use LocalStruct literal (type *LocalStruct) as type LocalInterface in assignment:
*LocalStruct does not implement LocalInterface (missing SomeMethod method)

huangapple
  • 本文由 发表于 2016年4月21日 11:10:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/36758900.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定