我想实现两个不同的接口(来自两个不同的包)。但它们之间存在冲突,就像这样:
type InterfaceA interface {
Init()
}
type InterfaceB interface {
Init(name string)
}
type Implementer struct {} // Wants to implement A and B
func (i Implementer) Init() {}
func (i Implementer) Init(name string) {} // Compiler complains
它说“方法重新声明”。一个结构如何实现两个接口?
正如已经回答的,这是不可能的,因为 Golang 不(并且可能不会)支持方法重载。
查看Golang常见问题解答:
其他语言的经验告诉我们,拥有相同名称但不同签名的多种方法有时是有用的,但在实践中也可能会造成混乱和脆弱。仅按名称匹配并要求类型一致是 Go 类型系统中的一个重大简化决策。
这是不可能的。
在 go 中你必须有一个单一的方法签名。
您应该重命名一种方法。
一个结构如何实现两个接口?
嗯......这取决于你是否可以在你的“一个结构”周围放置一个包装器。
如果您发现包装结构是不可接受的……那么其他贡献者所写的就是正确的!确实,Go 不允许这种方法重载。
但是,如果您包装
Implementer
类型...那么您可以毫无问题地实现这两个接口。
这是解决方法...
type InterfaceA interface {
Init()
}
type InterfaceB interface {
Init(name string)
}
type Implementer struct{} // Your one struct
type ImpA Implementer // Wrapped version to implement InterfaceA
type ImpB Implementer // Wrapped version to implement InterfaceB
func (i ImpA) Init() {}
func (i ImpB) Init(name string) {}
如果你去游乐场,你会发现它运行得很好!
方法签名必须匹配。如果您想要依赖注入,我会推荐功能选项模式。函数选项是返回在构造函数中循环调用的其他函数的函数。这是一个如何在 go 中使用功能选项和接口基础知识的示例。
package main
import (
"fmt"
"strconv"
)
type SomeData struct {
data string
}
// SomeData and SomeOtherData both implement SomeInterface and SomeOtherInterface
// SomeInterface and SomeOtherInterface both implement each other.
type SomeInterface interface {
String() string
Set(data string)
}
func (s *SomeData)String() string {
return s.data
}
func (s *SomeData)Set(data string) {
s.data = data
}
// SetDataOption is a functional option that can be used to inject a constructor dep
func SetDataOption(data string) func(*SomeData) {
return func(s *SomeData) {
s.Set(data)
}
}
// NewSomeData is the constructor; it takes in 0 to many functional options and calls each one in a loop.
func NewSomeData(options ...func(s *SomeData)) SomeInterface {
s := new(SomeData)
for _, o := range options {
o(s)
}
return s
}
//********************
type SomeOtherData struct {
data string
i int
}
type SomeOtherInterface interface {
String() string
Set(data string)
}
func (s *SomeOtherData)String() string {
return s.data + " " + strconv.Itoa(s.i)
}
func (s *SomeOtherData)Set(data string) {
s.data = data
}
func SetOtherDataOption(data string) func(*SomeOtherData) {
return func(s *SomeOtherData) {
s.Set(data)
}
}
func SetOtherIntOption(i int) func(*SomeOtherData) {
return func(s *SomeOtherData) {
s.i = i
}
}
// NewSomeOther data works just like NewSomeData only in this case, there are more options to choose from
// you can use none or any of them.
func NewSomeOtherData(options ...func(s *SomeOtherData)) SomeOtherInterface {
s := new(SomeOtherData)
for _, o := range options {
o(s)
}
return s
}
//*********************************
// HandleData accepts an interface
// Regardless of which underlying struct is in the interface, this function will handle
// either by calling the methods on the underlying struct.
func HandleData(si SomeInterface) {
fmt.Println(si) // fmt.Println calls the String() method of your struct if it has one using the Stringer interface
}
func main() {
someData := NewSomeData(SetDataOption("Optional constructor dep"))
someOtherData := NewSomeOtherData(SetOtherDataOption("Other optional constructor dep"), SetOtherIntOption(42))
HandleData(someData) // calls SomeData.String()
HandleData(someOtherData) // calls SomeOtherData.String()
someOtherData = someData // assign the first interface to the second, this works because they both implement each other.
HandleData(someOtherData) // calls SomeData.String() because there is a SomeData in the someOtherData variable.
}