接口方法不能在超类的子类中找到

时间:2016-11-21 03:10:56

标签: go

鉴于此代码......

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]

clonemain的来电自然完美无缺。

但是,在cloneAndGetName中,无法调用clone方法。 bs被指定为指向BaseStruct的指针,该指针与BaseItf方法具有clone接口。看起来sc中调用main的具体cloneAndGetName实例知道如何找到clone方法。

我错过了什么?有没有更好的方法来解决这个问题?在我的实际代码中,我需要一种从一些共享代码创建对象的新实例的方法。

4 个答案:

答案 0 :(得分:2)

bs.clone()失败,因为它试图致电bs.BaseItf1.clone()bs.BaseItf1nil。您无法从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{}}
}

https://play.golang.org/p/CWdScZMXZ_

答案 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())
}

https://play.golang.org/p/tHScVktrDZ