在Go语言中的隐式接口转换

huangapple go评论77阅读模式
英文:

Implicit interface conversion in golang

问题

这是我想要演示的想法的一个示例。

package main
import "fmt"

// 接口声明
type A interface {
	AAA() string
}

type B interface{
	Get() A
}

// 实现
type CA struct {}

// 实现 A.AAA 方法
func (ca *CA) AAA() string {
	return "it's CA"
}

type C struct {}

// 实现 B.Get 方法,返回一个结构体而不是接口
func (c *C) Get() *CA {
	return &CA{}
}

func main() {
	var c interface{} = &C{}
	d := c.(B)
	fmt.Println(d.Get().AAA())
	fmt.Println("Hello, playground")
}

在这个例子中:

  • 接口 B 有一个方法 Get,返回一个接口 A
  • 结构体 C 有一个成员函数 Get,返回一个指向结构体 CA 的指针,该结构体实现了接口 A

结果是,Go 无法从结构体 C 推断出接口 B,即使它们的 Get 方法只是返回类型不同,但是可以转换。

我提出这个问题的原因是当接口 A、B结构体 C、CA位于不同的包中时,我只能:

  • CGet 方法改为 func Get() A,这样会在包之间引入一些依赖关系。
  • 将接口 B 和结构体 CGet 方法都改为 func Get() interface{}

我想避免包之间的依赖关系,并尽量不依赖于 interface{},有人能给我一些提示吗?在 Go 中有什么最佳实践?

英文:

Here is an example of the idea I want to demonstrate.

package main
import "fmt"

// interface declaration
//

type A interface {
	AAA() string
}

type B interface{
	Get() A
}

// implementation
//

type CA struct {}

// implementation of A.AAA
func (ca *CA) AAA() string {
	return "it's CA"
}

type C struct {}

// implementation of B.Get, except for returning a 'struct' instead of an 'interface'
func (c *C) Get() *CA {
	return &CA{}
}

func main() {
	var c interface{} = &C{}
	d := c.(B)
	fmt.Println(d.Get().AAA())
	fmt.Println("Hello, playground")
}

In this example

  • interface B has a method Get to return an interface A
  • struct C has a member function Get to return a pointer to struct CA, which implements interface A

The result is Go can't deduce interface B from struct C, even their Get method is only different in returning type, which is convertible.

The reason I raise this question is when interface A, B and struct C, CA are in different packages, I can only:

  • refine the Get method of C to func Get() A, which introduce some dependency between packages.
  • refine both Get method of interface B and struct C to func Get() interface{}

I want to avoid dependency between packages and try not to rely on interface{}, can anyone give me some hint? What's the best practice in Go?

答案1

得分: 3

你当前的*C类型没有实现接口B,因此你不能将*C类型的值赋给B类型的变量,也不能从持有*C类型值的对象中进行“类型断言”以获取B类型的值。

以下是你可以做的。由于你已经在使用结构体字面量(&C{}),你可以声明c*C类型,然后调用它的Get()方法,并将C.Get()的返回值转换为A类型(因为返回值实现了A接口):

var c *C = &C{}
var a A = c.Get() // 这是可以的,隐式接口值创建(类型为A)
fmt.Println(a.AAA())
// 或者不使用中间变量"a",直接调用:
fmt.Println(c.Get().AAA())

输出结果:

it's CA
it's CA

或者进行重构:

问题在于你想要实现一个接口B,该接口具有返回另一个接口A的方法。要实现这个B接口,你必须依赖定义A的包,无法避免这一点。并且你必须声明C.Get()返回A(而不是具体的结构体类型)。

你可以将A移动到第三个包中,然后定义C的包只需要依赖这个第三个包,而不依赖定义B的包(但仍然会隐式实现接口类型B)。

英文:

Your current *C type does not implement the interface B, therefore you can't assign a value of *C to a variable of type B nor can't you "type assert" a value of B from something holding a value of type *C.

Here's what you can do. Since you're already using a struct literal (&C{}), you may declare c to be of type *C of which you can call its Get() method, and you can convert the return value of C.Get() to A (because the return value does implement A):

var c *C = &C{}
var a A = c.Get() // This is ok, implicit interface value creation (of type A)
fmt.Println(a.AAA())
// Or without the intermediate "a", you can simply call:
fmt.Println(c.Get().AAA())

Output:

it's CA
it's CA

Or refactor:

The problem is that you have an interface (B) which you want to implement, which has a method which returns another interface (A). To implement this B interface, you have to have dependency to the package that defines A, you can't avoid this. And you have to declare C.Get() to return A (instead of a concrete struct type).

You may move A to a 3rd package and then the package that defines C will only have to depend on this 3rd package, but will not depend on the package that defines B (but still will implicitly implement the interface type B).

huangapple
  • 本文由 发表于 2015年7月29日 15:43:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/31694238.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定