在 Go 中如何使用泛型 入门 - 限制泛型类型
通常,我们不希望或不需要对泛型使用的类型进行任何限制,因为我们不一定关心特定数据。 但是,其他时候,我们需要能够限制泛型使用的类型。 例如,如果我们正在创建一个泛型 Sorter 类型,则可能希望将其泛型类型限制为具有 Compare 方法的类型,以便 Sorter 可以比较它所持有的项目。 如果不包括该限制,则这些值甚至可能没有 Compare 方法,并且我们的 Sorter 将不知道如何比较它们。
在本节中,我们将为卡片创建一个新的 Card 接口,然后更新我们的 Deck 以仅允许添加卡片类型。
要开始更新,请打开 main.go 文件并添加 Card 接口:
import (
...
)
type Card interface {
fmt.Stringer
Name() string
}
我们的 Card 接口的定义与过去可能使用的任何其他 Go 接口相同; 将其与泛型一起使用没有特殊要求。 在此 Card 接口中,要想被视为 Card,它必须实现 fmt.Stringer 类型(它必须具有 Card 已有的 String 方法),并且它还必须具有返回 a 字符串值。
接下来,更新我们的 TradingCard 和 PlayingCard 类型以添加新的 Name 方法,除了现有的 String 方法,因此它们实现 Card 接口:
...
type TradingCard struct {
...
}
...
func (tc *TradingCard) Name() string {
return tc.String()
}
...
type PlayingCard struct {
...
}
...
func (pc *PlayingCard) Name() string {
return pc.String()
}
TradingCard 和 PlayingCard 已经有实现 fmt.Stringer
接口的 String 方法。 所以要实现 Card 接口,只需要添加新的 Name 方法即可。 另外,由于 fmt.Stringer 已经实现了返回卡片的名称,您可以只返回 Name 的 String 方法的结果。
现在,更新我们的 Deck,使其仅允许将 Card 类型用于 C:
...
type Deck[C Card] struct {
cards []C
}
在此更新之前,我们有 C any
用于类型限制(称为类型约束),这并不是什么限制。 由于 any
与 interface{}
的含义相同,因此它允许将 Go 中的任何类型用于 C 类型参数。 现在我们已经用新的 Card 接口替换了 any,Go 编译器将确保在编译程序时用于 C 的任何类型都实现 Card。
由于我们添加了此限制,现在可以在我们的 Deck 类型的方法中使用 Card 提供的任何方法。 如果我们希望 RandomCard 也打印出正在绘制的卡片的名称,它可以访问 Name 方法,因为它是 Card 接口的一部分。 我们将在下一篇文章中看到这一点。
这几个更新是需要进行的唯一更新,以将我们的 Deck 类型限制为仅使用 Card 值。 保存更改后,使用 go run
运行更新的程序:
$ go run main.go
输出的结果如下
--- drawing playing card ---
drew card: 5 of Clubs
card suit: Clubs
card rank: 5
--- drawing trading card ---
drew card: Droplets
card collectable name: Droplets
我们会看到,除了选择不同的卡之外,输出并没有改变。 由于我们的更新仅将值限制为已经使用的类型,因此程序的功能没有改变。
在本篇文章中,我们添加了新的 Card 接口并更新了 TradingCard 和 PlayingCard 来实现该接口。 还更新了 Deck 的类型约束以将其类型参数限制为仅实现 Card 接口的类型。
不过,到目前为止,我们只创建了一个通用结构类型。 除了创建泛型类型,Go 还允许你创建泛型函数。