Golang中如何定义接口并返回结构体

Golang中如何定义接口并返回结构体 我正在尝试模拟MongoDB客户端库mgo。这个库似乎没有使用接口来定义对其库的访问,因此我正在尝试模拟我们代码使用的方法。问题是当我有一个实现了方法的结构体,但签名不匹配时。通过代码更容易理解:

示例
type IFoo interface {
	foo() IBar
}

type IBar interface {
	bar()
}

func TakeMyInterface(x IFoo) {
	x.foo().bar()
}

// **********

type SomeUsefulThing struct{}

func (x *SomeUsefulThing) foo() *SomeUsefulThing {
	fmt.Print("SomeUsefulThing::foo was called")
	return x
}
func (x *SomeUsefulThing) bar() {
	fmt.Print("SomeUsefulThing::bar was called")
}

// **********
type MockSomeUsefulThing struct{}

func (x *MockSomeUsefulThing) foo() *MockSomeUsefulThing {
	fmt.Print("MockSomeUsefulThing::foo was called")
	return x
}
func (x *MockSomeUsefulThing) bar() {
	fmt.Print("MockSomeUsefulThing::bar was called")
}

// ********
func main() {
	x := &MockSomeUsefulThing{}
	x.foo().bar()
	TakeMyInterface(x)    // 这里出现错误
}

错误: 无法将x(类型*MockSomeUsefulThing)作为IFoo类型传递给TakeMyInterface: *MockSomeUsefulThing未实现IFoo(foo方法类型错误) 实际拥有:foo() *MockSomeUsefulThing 期望需要:foo() IBar

所以我的问题似乎是签名

func (x *MockSomeUsefulThing) foo() *MockSomeUsefulThing

func (x *MockSomeUsefulThing) foo() IBar

不相同。

但我认为这应该能工作,因为MockSomeUsefulThing确实实现了IBar。

对此有任何想法都会很有帮助,因为我原以为这在Go中是完全合理的做法。


更多关于Golang中如何定义接口并返回结构体的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

你有更完整的示例吗?

更多关于Golang中如何定义接口并返回结构体的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好

如果将返回类型定义为 IBar 就能正常工作

func (x *MockSomeUsefulThing) foo() IBar { ... }

这也许就是你想要的。返回一个实现了 IBar 接口的对象。或者我理解错了你的意思?

感谢您的建议。这对我的 MockSomeUsefulThing 有效,但如果我尝试将 SomeUsefulThing 传递给 TakeMyInterface 就会失败。在我的场景中,SomeUsefulThing 是 mongo 客户端类本身,因此我无法修改它。

y :=&SomeUsefulThing{}
y.foo().bar()
TakeMyInterface(y)

关于如何实现这部分功能,您有什么建议吗?

在Go语言中,接口的实现要求方法签名完全匹配,包括返回类型。虽然*MockSomeUsefulThing实现了IBar接口,但foo()方法返回的是具体类型*MockSomeUsefulThing而不是接口类型IBar,因此不满足IFoo接口的要求。

以下是修正后的代码示例:

package main

import "fmt"

type IFoo interface {
    foo() IBar
}

type IBar interface {
    bar()
}

func TakeMyInterface(x IFoo) {
    x.foo().bar()
}

// 修改SomeUsefulThing的foo方法返回IBar接口
type SomeUsefulThing struct{}

func (x *SomeUsefulThing) foo() IBar {
    fmt.Println("SomeUsefulThing::foo was called")
    return x
}

func (x *SomeUsefulThing) bar() {
    fmt.Println("SomeUsefulThing::bar was called")
}

// 修改MockSomeUsefulThing的foo方法返回IBar接口
type MockSomeUsefulThing struct{}

func (x *MockSomeUsefulThing) foo() IBar {
    fmt.Println("MockSomeUsefulThing::foo was called")
    return x
}

func (x *MockSomeUsefulThing) bar() {
    fmt.Println("MockSomeUsefulThing::bar was called")
}

func main() {
    x := &MockSomeUsefulThing{}
    x.foo().bar()
    TakeMyInterface(x) // 现在可以正常编译运行
}

另一种方案是使用泛型来保持具体类型的返回:

package main

import "fmt"

type IBar interface {
    bar()
}

type IFoo[T IBar] interface {
    foo() T
}

func TakeMyInterface[T IBar](x IFoo[T]) {
    x.foo().bar()
}

type SomeUsefulThing struct{}

func (x *SomeUsefulThing) foo() *SomeUsefulThing {
    fmt.Println("SomeUsefulThing::foo was called")
    return x
}

func (x *SomeUsefulThing) bar() {
    fmt.Println("SomeUsefulThing::bar was called")
}

type MockSomeUsefulThing struct{}

func (x *MockSomeUsefulThing) foo() *MockSomeUsefulThing {
    fmt.Println("MockSomeUsefulThing::foo was called")
    return x
}

func (x *MockSomeUsefulThing) bar() {
    fmt.Println("MockSomeUsefulThing::bar was called")
}

func main() {
    x := &MockSomeUsefulThing{}
    x.foo().bar()
    TakeMyInterface(x) // 使用泛型版本
}

第一个方案通过修改方法签名返回接口类型来满足接口要求,这是最直接的解决方案。第二个方案使用Go 1.18引入的泛型特性,可以在保持原有方法签名的同时实现接口约束。

回到顶部