golang 如何定义一种具有比较能力的Interface

部署运行你感兴趣的模型镜像

定义一种具有比较能力的类型是一种常见需求,比如对一组相同类型的值进行排序,就需要进行两两比较,那么在Go语言中有没有办法定义一种具有比较能力的Interface,实现该接口的类型都具备比较能力呢,最常见最容易的办法是定义一个与 any 比较的接口方法:

type Comparable interface {
	func LessThen(a any) bool
}

func Sort[T Comparable] (s []T) {
	// s[i].LessThen(s[j])
}

具体实现的时候进行类型断言:

type User struct {
	Age int
}

func(u *User) LessThen(a any) bool {
	u2, ok := a.(*User)
	if !ok {
		return false
	}
	return u.Age < u2.Age
}

多少缺点意思,没办法约束比较目标的类型与自己相同,于是我们想到可以利用 Go1.18 引入的范型来约束比较目标类型,于是 Comparable 定义改进为:

type Comparable[T any] interface {
	func LessThen(t T) bool
}

实现改变为:

// 实现接口 Comparable[*User]
func(u *User) LessThen(u2 *User) bool {
	return u.Age < u2.Age
}

随之 Sort 方法也要修改:

func Sort[T Comparable[*User]] (s []T) {
	// s[i].LessThen(s[j])
}

但这样的 Sort 方法限制了 T 只能与 *User 比较,要想 T 具有与自身类型比较的能力,Sort 定义需要再次修改:

func Sort[T Comparable[T]] (s []T) {
	// s[i].LessThen(s[j])
}

这样的 Sort 方法定义就比较通用了,基本实现了我们最初的目标,虽然看起来有点晦涩难懂,但好在接口实现比较简单, 比如:

type Int int

func (i Int) LessThen(o Int) bool {
    return i < o
}

Sort([]Int{3, 5, 7})

type Time time.Time

func (t Time) LessThen(t2 Time) bool {
	return time.Time(t).Before(time.Time(o))
}

Sort([]Time{Time(time.Now()), Time(time.Now().Add(-time.Minute))})

期待后面版本的 Go 语言的 Interface 和范型加入对 self 的支持,这样的话我们的接口定义就会变得简单直接,比如:

// 伪代码,无法编译

// 限制它的实现必须是与实现者自身类型比较,而不是其他类型
type Comparable interface {
	func LessThen(t self) bool
}

Golang 2 目前有一个提案就是讨论关于 self的: https://github.com/golang/go/issues/28254,有兴趣也可以关注参与

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

### Golang 中的接口概念 在 Go 语言中,`interface` 是一种抽象类型,用于定义对象的行为而不是其具体实现[^1]。接口允许程序员指定任何实现了特定方法集合的对象都可以被当作该接口类型的实例来使用。 #### 接口声明 一个简单的接口可以通过下面的方式创建: ```go type Speaker interface { Speak() string } ``` 这段代码定义了一个名为 `Speaker` 的接口,它只包含一个叫做 `Speak()` 方法签名的方法。这意味着只要某个结构体提供了此函数作为成员,则该结构体会自动满足这个接口的要求而无需显式注册或继承关系。 #### 实现接口 为了使某类能够符合给定的接口标准,只需要确保其实现了所有由那个接口所规定的方法即可。这里有一个例子展示了如何让两个不同的类型都成为上述 `Speaker` 接口的一部分: ```go // 定义第一个类型并实现接口方法 type Dog struct{} func (d Dog) Speak() string { return "Woof!" } // 定义第二个类型同样实现同样的接口方法 type Cat struct{} func (c Cat) Speak() string { return "Meow~" } ``` 此时无论是 `Dog{}` 还是 `Cat{}` 都已经隐含地成为了 `Speaker` 类型的一员,因为它们各自拥有匹配于 `Speaker` 所需的一切行为特征——即实现了相应的 `Speak()` 函数。 #### 使用接口变量 一旦有了多个不同种类的数据源都能适配同一个接口之后,就可以方便地操作这些数据而不必关心具体的底层细节了。比如现在要遍历一系列动物发出声音的动作,只需传入相应切片形式的参数列表就可完成任务: ```go func main() { var animals []Speaker dog := Dog{} cat := Cat{} animals = append(animals, dog) animals = append(animals, cat) for _, animal := range animals { fmt.Println(animal.Speak()) } } ``` 以上程序会依次打印出 `"Woof!"` 和 `"Meow~"` 字符串,这表明尽管实际存储的是两种完全不一样的实体(狗和猫),但在调用 `.Speak()` 成员时却能获得一致的结果集,这就是多态性的体现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值