英文:
Go Interface Method Returning Interface Doesn't Match Method Returning Concrete Type
问题
我正在尝试让Go接口正常工作,我遇到的问题是,如果一个Go接口方法返回一个Go接口,只有在实现声明接口而不是实现接口的具体类型时才能正常工作。
在下面的示例中,我收到以下错误信息:
> prog.go:29: cannot use &f (type *Bar) as type StringerGetter in
> argument to Printer: *Bar does not implement StringerGetter (wrong
> type for GetStringer method) have GetStringer() *Foo want
> GetStringer() fmt.Stringer
package main
import "fmt"
type Foo struct {
val string
}
func (f *Foo) String() string {
return f.val
}
type StringerGetter interface {
GetStringer() fmt.Stringer
}
type Bar struct{}
func (b *Bar) GetStringer() *Foo {
return &Foo{"foo"}
}
func Printer(s StringerGetter) {
fmt.Println(s.GetStringer())
}
func main() {
f := Bar{}
Printer(&f)
}
然而,如果我将 func (b *Bar) GetStringer() *Foo {
改为 func (b *Bar) GetStringer() fmt.Stringer {
,它会按预期工作。
Foo
确实满足 fmt.Stringer
接口。
问题在于上述示例代码只是说明问题的简单示例。
在我尝试编写的代码中,我创建的接口位于我的 main
包中,而我要编写接口的具体类型位于第三方包中,我不希望修改该包。
我希望能够编写自己的结构体来满足这些接口,因此也不能针对具体的返回类型进行接口化。
我必须假设有一种方法可以做到这一点。
英文:
I'm trying to get Go interfaces to work, and the problem I am having is that a Go interface method returning a go interface only works if the implementation declares the interface and not a concrete type that implements the interface.
In the following example I receive:
> prog.go:29: cannot use &f (type *Bar) as type StringerGetter in
> argument to Printer: *Bar does not implement StringerGetter (wrong
> type for GetStringer method) have GetStringer() *Foo want
> GetStringer() fmt.Stringer
package main
import "fmt"
type Foo struct {
val string
}
func (f *Foo) String() string {
return f.val
}
type StringerGetter interface {
GetStringer() fmt.Stringer
}
type Bar struct{}
func (b *Bar) GetStringer() *Foo {
return &Foo{"foo"}
}
func Printer(s StringerGetter) {
fmt.Println(s.GetStringer())
}
func main() {
f := Bar{}
Printer(&f)
}
However if I change func (b *Bar) GetStringer() *Foo {
to func (b *Bar) GetStringer() fmt.Stringer {
it works as expected.
Foo
does fulfill fmt.Stringer
The rub is that the above is simple example code illustrating the problem.
In the case of the code I am trying to write, the interfaces I created are in my "main"
package, whereas the concrete types I'm writing interfaces against are in a 3rd party package I'd prefer not to modify.
I want to be able to write my own structs as well that fulfill said interfaces, so interfacing against the concrete return type isn't an option either.
I have to assume there is a way to do this.
答案1
得分: 4
你的问题是,你试图使别人的类型(即另一个包的类型)符合一个它实际上并不符合的接口。你不能这样做。但是你可以使你的类型符合任何你想要的接口。你可以使你的类型包装他们的类型。有两种方法可以做到这一点。这是其中一种方法:
type MyBar Bar
func (b *MyBar) GetStringer() fmt.Stringer {
return &Foo{"foo"}
}
func main() {
f := MyBar(Bar{})
Printer(&f)
}
现在我们创建了一个新类型MyBar
,它包装了一个Bar
并提供了你想要的接口。然而,这种方法不会“继承”Bar
的任何其他功能。你必须包装你想要的每个单独的东西,并且你可能需要重新实现(因为你无法访问底层实现)。
但是你也可以嵌入Bar
,并且“继承”你没有改变的所有内容,并且可以访问底层实现(这可能更接近你在这里想要的)。例如:
type MyBar struct{ Bar }
func (b *MyBar) GetStringer() fmt.Stringer {
return b.Bar.GetStringer()
}
func main() {
f := MyBar{Bar{}}
Printer(&f)
}
现在,如果Bar
有其他方法,它们将自动显示在MyBar
上。根据你的问题,任何一种方法都可能有用。你可以使用这种技术来帮助其他包的类型符合你想要的接口。
这并不意味着Bar
和MyBar
是可互换的,但它们非常接近,你可以根据需要很容易地在它们之间进行转换。
英文:
Your problem is that you're trying to cause someone else's (i.e. another package's) type to conform to an interface that it doesn't actually conform to. You can't do that. But you can make your type conform to anything you want. And you can make your type wrap their type. There are two ways to do this. Here's one:
type MyBar Bar
func (b *MyBar) GetStringer() fmt.Stringer {
return &Foo{"foo"}
}
func main() {
f := MyBar(Bar{})
Printer(&f)
}
Now we've created a new type, MyBar
that wraps a Bar
and provides the interface you want. This approach doesn't "inherit" any other functionality from Bar
, however. You have to wrap every individual thing you want, and you may have to reimplement (because you don't have access to the underlying impl).
But you can also embed Bar
, and "inherit" everything you don't change and get access to the underlying impl (which is probably closer to what you want here). For example:
type MyBar struct{ Bar }
func (b *MyBar) GetStringer() fmt.Stringer {
return b.Bar.GetStringer()
}
func main() {
f := MyBar{Bar{}}
Printer(&f)
}
Now if Bar
has additional methods, they'll show up on MyBar
for free. Either can be useful depending on your problem. You can use this technique to help other packages types conform to whatever you want.
This doesn't mean that Bar
and MyBar
are interchangeable, but they're pretty close and it's pretty easy to convert between them as you need.
答案2
得分: 2
“我想要”和Go语言并不搭配。
我必须假设有一种方法可以做到这一点。
不,没有这种方法。接口必须完全匹配。Go语言没有超类型或特殊化的概念。
英文:
> I want to be able to write my own structs as well that fulfill said interfaces, so interfacing against the concrete return type isn't an option either.
"I want" and Go does not match well.
> I have to assume there is a way to do this.
No, there is not. Interfaces match exactly. Go has no notion of supertype or specialisation.
答案3
得分: 1
正如其他人所说,Go语言不支持你正在尝试的操作。
你是否考虑过你的设计可能有点过于复杂?
在Go语言中,你不会像在其他语言(比如Java)中那样经常遇到工厂的工厂模式。
至少,我认为你正在通过返回接口的接口来实现工厂的工厂模式。
你可以创建一个新的包来保存工厂的工厂接口。
但通常情况下,我会首先尝试看看这种抽象层次是否值得在代码库中带来可读性的损失。可能有一种更简单的方法来解决你的问题。
英文:
As others have stated, Go does not support what you are trying to do.
Have you considered that you may be over complicating your design a bit?
You don't often find factory of factory patterns in Go like you would in other languages like Java (for example).
At least, that's what I assume you are doing by having interfaces that return interfaces that return a thing.
You could always make a new package to hold the factory factory interfaces.
But usually, I'd first try to see if that level of abstraction is worth the readability hit you will take in the code base. There may be a simpler way to solve your problem.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论