鉴于此代码......
type BaseItf1 interface {
getName() string
clone() *BaseStruct
}
type BaseStruct struct {
BaseItf1
}
func (bs *BaseStruct) cloneAndGetName() string {
sc := bs.clone()
return sc.getName()
}
type SubClass struct {
BaseStruct
}
func (sc *SubClass) getName() string {
return "A"
}
func (sc *SubClass) clone() *SubClass {
return &SubClass{}
}
func main() {
sc := &SubClass{}
fmt.Printf("-> %s\n", sc.clone().getName())
fmt.Printf("-> %s\n", sc.cloneAndGetName())
}
我无法弄清楚为什么我会收到此错误:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0xffffffff addr=0x0 pc=0x2004a]
clone
中main
的来电自然完美无缺。
但是,在cloneAndGetName
中,无法调用clone
方法。 bs
被指定为指向BaseStruct
的指针,该指针与BaseItf
方法具有clone
接口。看起来sc
中调用main
的具体cloneAndGetName
实例知道如何找到clone
方法。
我错过了什么?有没有更好的方法来解决这个问题?在我的实际代码中,我需要一种从一些共享代码创建对象的新实例的方法。
答案 0 :(得分:2)
bs.clone()
失败,因为它试图致电bs.BaseItf1.clone()
而bs.BaseItf1
为nil
。您无法从clone()
类型的变量中调用为*SubClass
定义的*BaseStruct
。在Go中嵌入类型与在其他语言中的子类化不同。
答案 1 :(得分:1)
您将嵌入与界面实现混淆。在当前状态下,您有一个结构SubClass
,它嵌入结构BaseStruct
,然后嵌入一个接口BaseItf1
。但是,您遇到问题:SubClass
未覆盖cloneAndGetName()
方法。因此,在嵌入式BaseStruct
结构上调用此方法。使用嵌入式结构作为接收器调用嵌入式方法调用,而不是嵌入结构。因此BaseStruct
结构只能访问自己的方法,而不能包含它的SubClass
结构。由于这些方法本身是嵌入式BaseItf1
接口的结果,BaseStruct
会在该嵌入式接口上调用这些方法,即nil
。当然,这会触发段错误。
在这种情况下,您似乎希望在某个基础结构上定义标准方法,但是能够使用子类覆盖该行为。在这种情况下,请将SubClass
的实例放入BaseClass
:
sc := BaseStruct{&SubClass{}}
fmt.Printf("-> %s\n", sc.clone().getName())
fmt.Printf("-> %s\n", sc.cloneAndGetName())
在这种情况下,sc.closeAndGetName()
将调用BaseStruct
,但clone()
和getName()
调用将在子结构上发生。
如果接口为nil,您甚至可以定义默认行为:
type BaseItf1 interface {
getName() string
clone() *BaseStruct
}
type BaseStruct struct {
sub BaseItf1
}
func (bs *BaseStruct) cloneAndGetName() string {
sc := bs.clone()
return sc.getName()
}
func (bs *BaseStruct) getName() string {
if bs.sub != nil {
return bs.sub.getName()
}
// default behavior
return "<nil>"
}
func (bs *BaseStruct) clone() *BaseStruct {
if bs.sub != nil {
return bs.sub.clone()
}
// default behavior
return bs
}
type SubClass struct {
// Doesn't need to embed BaseStruct
// ...
}
func (sc *SubClass) getName() string {
return "A"
}
func (sc *SubClass) clone() *BaseStruct {
return &BaseStruct{&SubClass{}}
}
答案 2 :(得分:0)
您应首先在界面中实现该功能。
package main
import (
"fmt"
)
type BaseItf1 interface {
getName() string
clone() *BaseStruct
}
type BaseStruct struct {
BaseItf1
}
func (bs *BaseStruct) cloneAndGetName() string {
sc := bs.clone()
return sc.getName()
}
func (bs *BaseStruct) getName() string {
return ""
}
func (bs *BaseStruct) clone() *BaseStruct {
return nil
}
type SubClass struct {
BaseStruct
}
func (sc *SubClass) getName() string {
return "A"
}
func (sc *SubClass) clone() *SubClass {
return &SubClass{}
}
func main() {
sc := &SubClass{}
fmt.Printf("-> %s\n", sc.clone().getName())
fmt.Printf("-> %s\n", sc.cloneAndGetName())
}
答案 3 :(得分:0)
当你有一个结构并且你想要满足一个接口时你必须实现该接口的所有方法,当你有一个内部有其他接口的接口时它会有所不同,对于结构它只是拥有字段,如果您签入代码sc.BaseItf1
具有类型和值,则对.clone()
和.cloneAndGetName()
的调用失败。在这里实现了方法:
type BaseItf1 interface {
getName() string
clone() *BaseStruct
}
type BaseStruct struct {
BaseItf1 // shortcut to BaseItf1 BaseItf1 <fieldName> <Type>
}
func (bs *BaseStruct) clone() *BaseStruct {
return &BaseStruct{}
}
func (bs *BaseStruct) getName() string {
//
// maybe BaseStruct should have a field Name, not sure if you want to
// in that case we can do:
//
// return bs.Name
return "a BaseStruct has no name"
}
func (bs *BaseStruct) cloneAndGetName() string {
sc := bs.clone()
return sc.getName()
}
type SubClass struct {
BaseStruct
}
func (sc *SubClass) methodA() string {
return "A"
}
func (sc *SubClass) methodB() *SubClass {
return &SubClass{}
}
func main() {
sc := &SubClass{}
fmt.Printf("type: %T and value: %v \n\n", sc.BaseItf1, sc.BaseItf1)
fmt.Printf("-> %q\n", sc.clone().getName())
fmt.Printf("-> %q\n", sc.cloneAndGetName())
}