Go interface遇到的几个小问题(持续更新)

2021-02-01  本文已影响0人  小东班吉

interface遇到的几个问题

Cannot use 'printJobEvent' (type PrintJobEvent) as type ImportInterface Type does not implement 'ImportInterface' as 'NewData' method has a pointer receiver

先上代码:

type ImportInterface interface {
    NewData(line *[]string, heads *[]string, operators map[string]int64)
    Import(reader io.Reader, heads *[]string) (int64, error)
}

func (ic ImportCollect) Import(i ImportInterface) (int64, error) {
    return i.Import(ic.File, ic.Heads)
}

//printjobmodel
func (record *PrintJobEvent) Import(reader io.Reader, heads *[]string) (int64, error) {
    // ...
}


var printJobEvent = printjobmodel.PrintJobEvent{}
importCollect := importmodel.NewImportCollect(heads, name, charset, file)
count, err = importCollect.Import(printJobEvent)

importCollect.Import(printJobEvent)这里编译错误,翻译过来就是不能使用printJobEvent,printJobEvent未实现ImportInterface,因为NewData的接收者是一个指针类型。

当一个类型实现了接口的方法的时候,就认为实现了该接口,同时该类型的值会存到接口类型的值,对接口值方法的调用就会调用起接口值里存储的用户定义类型的值的方法。

可以粗暴的理解为,接口也有了类型的方法,那么问题就简单了,调用方法的时候会发生一个值拷贝,方法的接收者是一个指针,用指针去调用那拷贝的就是一个指针,两个指向同一个类型,可以调用该类型的方法,如果我们用值去调用,那拷贝的就是一个值了,而很显然值不是指针,即使编译器给创建一个指针,那指向的也不是原来的方法。

那么从值的角度来看,当一个值去调用类型的方法的时候,那它的方法集就只有(t T),但如果是一个指针调用,那方法集就是(t T) 和 (t *T),所以上面的问题改成 importCollect.Import(&printJobEvent) 即可

invalid memory address or nil pointer dereference

这个问题一眼看到就感觉是指针没有分配内存地址的问题。但也不全是,代码如下:

type ImportInterface interface {
  NewData(line *[]string, heads *[]string, operators map[string]int64)
    Import(data []interface{}) (int64, error)
}

func (record *PrintJobEvent) NewData(line *[]string, heads *[]string, operators map[string]int64) {
    ...
}

func (record PrintJobEvent) Import(data []interface{}) (int64, error) {
    importData := make([]*PrintJobEvent, len(data))
    // 导入
    for k, v := range data {
        t, ok := v.(PrintJobEvent)
        if ok {
            importData[k] = &t
        }
    }
    return MultipleAdd(&importData)
}

func (ic ImportCollect) Import(i ImportInterface) (int64, error) {
    importData := make([]interface{}, 0)
    for {
        ...
        i.NewData(&line, ic.Heads, operators)
        importData = append(importData, i)
    }
    return i.Import(importData)
}

var printJobEvent = printjobmodel.PrintJobEvent{}
count, err = importCollect.Import(printJobEvent)

代码编译是没有任何问题,但是在importData = append(importData, i)这里的时候i其实是一个指针类型,因为i.NewData的接收者是一个指针,所以在 t, ok := v.(PrintJobEvent) 这里类型断言的时候是错误的,永远不会转换成功,正确的应该是t, ok := v.(*PrintJobEvent)

而上面代码之所以说内存地址无效,主要就是因为断言永远不会成功,所以importData是一个存储了指针的切片,而这些指针永远都是nil,所以在下文遍历improtData调用的时候等于类似nil.Name这样的,所以才有了上述的错误。

上一篇下一篇

猜你喜欢

热点阅读