我在界面中遇到恐慌:运行时错误:无效的内存地址或零指针取消引用。尽管我尝试了各种方法和解决方案来使其发挥作用,但它们并没有像我预期的那样发挥作用。我不知道问题出在哪里。
这是我收到的错误:
This is the directory1 function
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x20 pc=0x47ae8a]
goroutine 1 [running]:
github.com/ibilalkayy/test/two/directory1.MyInterfaces.CallDirectory2(...)
/mnt/d/go/src/github.com/ibilalkayy/test/two/directory1/directory1.go:19
github.com/ibilalkayy/test/two/directory1.MyInterfaces.Directory1({{0x0?, 0x0?}})
/mnt/d/go/src/github.com/ibilalkayy/test/two/directory1/directory1.go:15 +0x6a
main.main()
/mnt/d/go/src/github.com/ibilalkayy/test/two/main.go:12 +0x17
exit status 2
main.go 文件
package main
import (
"github.com/ibilalkayy/test/two/directory1"
"github.com/ibilalkayy/test/two/directory2"
)
func main() {
myDirectory1 := directory1.MyInterfaces{}
myDirectory2 := directory2.MyInterfaces{}
myDirectory1.Directory1()
myDirectory2.Directory2()
}
interfaces.go 文件
package interfaces
type InterfaceDirectory1 interface {
Directory1()
}
type InterfaceDirectory2 interface {
Directory2()
}
type AllInterfaces interface {
InterfaceDirectory1
InterfaceDirectory2
}
目录1.go文件
package directory1
import (
"fmt"
"github.com/ibilalkayy/test/two/interfaces"
)
type MyInterfaces struct {
interfaces.AllInterfaces
}
func (m MyInterfaces) Directory1() {
fmt.Println("This is the directory1 function")
m.CallDirectory2()
}
func (m MyInterfaces) CallDirectory2() {
m.Directory2()
}
directory2.go 文件
package directory2
import (
"fmt"
"github.com/ibilalkayy/test/two/interfaces"
)
type MyInterfaces struct {
interfaces.AllInterfaces
}
func (m MyInterfaces) CallDirectory1() {
m.Directory1()
}
func (m MyInterfaces) Directory2() {
m.CallDirectory1()
fmt.Println("This is the directory2 function")
}
是的,正如 kostix 在他们的评论中指出的那样,您的代码读起来好像围绕 golang 的接口可能存在一些混乱。其一,通常不赞成创建太大的接口,而且集中接口并不是一个好主意。在使用的地方声明您想要/需要使用的接口,不要导入包含接口的单独包,这些接口又在另一个包/模块中实现。
无论如何,恐慌的原因很简单:
type MyInterfaces struct {
AllInterfaces
}
这声明了一个嵌入
AllInterfaces
类型的结构类型。这并不意味着您的类型“继承”接口。这并不意味着您的结构实现接口。这只是意味着它通过嵌入类型暴露/导出。
当你写下:
foo := MyInterfaces{}
您将此结构类型分配给变量,但没有初始化嵌入的
AllInterfaces
类型,因此您的结构本质上将其内部字段初始化为 nil
。请注意,像这样的嵌入类型基本上意味着编写:
foo.Directory1()
相当于:
foo.AllInterfaces.Directory1()
因此,即使您已经嵌入了类型,您也必须将其视为结构中的隐含字段。如果
MyInterfaces
类型上没有冲突的名称,则任何嵌入类型导出的字段/方法都可以访问,就像它们是外部类型上的字段一样,但如果存在名称冲突,则需要指定确切的名称您想要访问的字段或方法:
type Foo struct {
Name string
Age uint
}
type Bar struct {
Name string
Size uint
}
type Foobar struct {
Foo
Bar
}
fb := Foobar{}
fb.Age = 123 // fine, sets Foobar.Foo.Age
fb.Size = 10 // fine, sets Foobar.Bar.Size
fb.Name // which one? Foobar.{Foo,Bar}.Age?
// you need to be specific here...
fb.Foo.Name = "Foo name"
fb.Bar.Name = "Bar name"
// or initialise when assigning:
fb = Foobar{
Foo: Foo{
Name: "Foo name",
Age: 123,
},
Bar: Bar{
Name: "Bar name",
Size: 10,
},
}
您正在嵌入一个接口,因此与上面的示例(嵌入可以初始化的结构)不同,编译器无法将嵌入类型初始化为
nil
以外的任何类型。这意味着:
foo := MyInterfaces{}
相当于:
foo := MyInterfaces{
AllInterfaces: nil,
}
因此,调用
foo.Directory1()
相当于 foo.AllInterfaces.Directory1()
,实际上是: foo.AllInterfaces.InterfaceDirectory1.Directory1()
(但接口组成有点不同,所以现在我们坚持使用 foo.AllInterfaces.Directory1()
)
现在让我们填空:
foo
已初始化为 MyInterfaces{nil}
foo.Directory1()
相当于foo.AllInterfaces.Directory1()
foo.Directory1()
本质上是 foo.nil.Directory1()
通过调用
nil
上的方法(该方法未实现相关方法),您会遇到运行时恐慌。解决方案是通过提供 MyInterfaces
的实际实现来正确初始化 AllInterfaces
:
// assume svc is a type that implements AllInterfaces:
foo := MyInterfaces{
AllInterfaces: svc,
}
foo.Directory1() // works fine
在处理嵌入类型时,尤其是接口时,提供构造函数是很常见的:
func NewMyInterfaces(all AllInterfaces) *MyInterfaces {
return &MyInterfaces{
AllInterfaces: all,
}
}
这会让你的代码看起来更干净:
foo := NewMyInterfaces(svc)
如果
svc
不实现该接口,它将生成编译时错误