Golang:如何嵌入具有不同类型参数的接口?

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

Golang: How to embed interface with different type parameters?

问题

代码给我报错:DB 重新声明
有没有一种惯用的方法来解决这个问题?或者有没有任何变通方法?

谢谢提前帮助。

type a struct {
	DB[int64]
	DB[string]
}

type b interface {
	DB[int64]
	DB[string]
}

type DB[T any] interface {
	GetList(query string) ([]T, error)
}
英文:

The code gives me error: DB redeclared.
Is there any idiomatic way to solve it? Or any work-around?

TIA

type a struct {
	DB[int64]
	DB[string]
}

type b interface {
	DB[int64]
	DB[string]
}

type DB[T any] interface {
	GetList(query string) ([]T, error)
}

答案1

得分: 4

你不能嵌入相同的接口,即使使用不同的类型参数也不行。无论如何实例化,你都试图将具有相同名称GetList但具有不同签名的两个方法(由DB的不同实例化给出)提升到b接口中。

尽管在技术上不完全相同,但在结构体中嵌入的情况类似。在结构体中,嵌入字段的名称是类型的名称-DB,而结构体不能有两个具有相同名称的非空字段。

关于如何解决这个问题,取决于你想要实现什么。

如果你想传达“a使用任一类型参数实现了DB”,你可以嵌入DB[T]并使a本身成为泛型,并限制a的类型参数:

type a[T int64 | string] struct {
    DB[T]
}

// DB[T]接口的示例实现
// 如果你嵌入了DB[T],你可能不会将`a`本身作为接收器使用
func (r *a[T]) GetList(query string) ([]T, error) {
    // 这也是泛型方法
}

这是可以的,因为DB的类型参数被限制为any,而a的类型参数更加具体。这允许你在其他泛型方法中使用a,或者在实例化时选择特定的类型,但GetList的实现也必须是参数化的。

否则,如果你需要a具有返回int64string分离方法,你必须给它们不同的名称。

最后,你可以将DB的实例嵌入到具有不同名称的两个接口中,然后将它们嵌入到a中。

type a struct {
	DBStr
	DBInt
}

type DBStr interface {
	DB[string]
}

type DBInt interface {
	DB[int64]
}

这种方式下,顶级选择器不可用,因为方法名称仍然相同。程序可以编译,但你必须明确选择在哪个字段上调用该方法:

myA := a{ /* 初始化两个字段 */ }

res, err = myA.DBStr.GetList(query)
// res 的类型是 []string
// 或者
res, err = myA.DBInt.GetList(query)
// res 的类型是 []int64
英文:

You can't embed the same interface, even with different type parameters. Regardless of how it is instantiated, you are trying to promote into the b interface two methods with the same name GetList and different signatures — given by the different instantiations of DB.

The situation is similar, although not technically the same, for embedding into a struct. In structs, the name of the embedded field is the name of the type — DB —, and a struct can't have two non-blank fields with the same name.

About how to solve this issue, it depends what you want to accomplish.

If you want to convey that "a implements DB with either type parameter" you can embed DB[T] and make a itself generic, and restrict a's type parameters:

type a[T int64 | string] struct {
    DB[T]
}

// illustrative implementation of the DB[T] interface
// if you embed DB[T] you likely won't use `a` itself as receiver
func (r *a[T]) GetList(query string) ([]T, error) {
    // also generic method
}

This is okay because DB's type parameter is constrained to any, and a's type parameter is more restrictive. This allows you to use a in other generic methods, or choose a specific type upon instantiation, but the implementation of GetList has to be parametrized too.

Otherwise if you need a to have separated methods that return int64 or string, you must give it different names.

Finally, you can embed instances of DB into two interfaces with different names, and then embed those into a instead.

type a struct {
	DBStr
	DBInt
}

type DBStr interface {
	DB[string]
}

type DBInt interface {
	DB[int64]
}

This way though the top-level selector isn't available because the method names are still the same. The program compiles, but you'll have to explicitly choose which field to call the method on:

myA := a{ /* init the two fields */ }

res, err = myA.DBStr.GetList(query)
// res is type []string
// or
res, err = myA.DBInt.GetList(query)
// res is type []int64

huangapple
  • 本文由 发表于 2022年9月19日 18:29:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/73771990.html
匿名

发表评论

匿名网友

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

确定