使用接口定义的回调参数,使用实现调用

时间:2017-06-28 17:20:52

标签: go

我想做的事情:

我有一个库包,它定义了几种类型,都实现了给定的接口。在整个代码中,涉及回调,而不是在我为其定义类型的任何地方编写回调类型。

type Foo interface {
    Bar()
}

type MyCallback func(f Foo)

type CoolFoo int

type BadFoo int

func (cf *CoolFoo) Bar(cb MyCallback) {
}

func (bf *BadFoo) Bar(cb MyCallback) {
}

然后从使用该库的客户端代码开始,我想使用回调调用。如果我使用接口类型调用它,它可以工作:

cf := &CoolFoo{}

cf.Bar(func(f packageName.Foo) {
})

但我宁愿在我的IDE中有更多的自我记录代码和正确的类型提示,所以我尝试使用implementsor类型调用它,例如:

cf := &CoolFoo{}

cf.Bar(func(f packageName.CoolFoo) {
})

无法编译,错误:

  

不能使用func literal(类型为func(packageName.CoolFoo))作为类型   cf.Bar

参数中的packageName.MyCallback

这是不可能的,还是我犯了一些假的错误?我在开发方面不是很有经验,而且我已经尝试过环顾四周,但在这里找不到解决方案。

我发现它将它作为Foo或界面{}传递,然后在回调中投射到我想要的内容,但我想避免它,因为它感觉很乱

感谢您的帮助

1 个答案:

答案 0 :(得分:3)

函数签名是func(f Foo),因此正是必须传递的内容。原因很简单:如果您的方法需要该签名的回调函数,它可以传入任何实现Foo 的类型。如果您传入的函数只接受CoolFoo,则调用者仍然可以传递BadFoo,因为您定义的类型允许它。因此,编译器要求类型精确匹配。同样,如果您的回调函数试图转换为具体类型,但这不是传递给它的类型,它将失败,因为您正在做出代码不支持的假设。

如果您的回调只会传入CoolFoo,那么这可能是您在签名中使用的类型。如果CoolFooBadFoo每个只会将各自的类型传递给他们的回调,那么您可能需要两个不同的回调签名。如果它们真的可以互换,那么你描述的情况(专门用CoolFoo执行的函数)不是语言的问题,这是你设计的一个问题。