英文:
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
具有返回int64
或string
的分离方法,你必须给它们不同的名称。
最后,你可以将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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论