go语言函数/方法入参对象方法 接口interface约束示例

go语言使用泛型接口约束可以约束指定的对象参数 必须具备指定的方法 或者必须实现了指定的接口 1个或者多个接口(通过接口继承实现), 这个在面向api的接口编程中应用非常广泛,也非常实用,废话不多说,直接上代码:

泛型约束 接口 方法 接口继承 示例代码

Go 复制代码
//文件名 person.go
package genricargs

import "fmt"

// 泛型接口约束方法/函数形参,可以约束形参对象必须拥有指定的方法才能调用,
// 如 func SpeechDemo2[T interface {Speech() string; Exam()}](people T)
// 形参方法约束如果有多个可以每个一行或者在当行使用 分号 ; 隔开
// 这里的[T interface {Speech() string; Exam()}]约束的是 T中的方法,只要T中有这里指定的方法即可,
// 如果 T后面跟的 interface{ 方法定义1,方法定义2} 这表示T必须具备指定的方法, 如 [T interface {Speech() string; Exam()}]
// 如果 T后面跟的是接口,则说明T必须是实现指定的接口的对象, 如  [T Ability]
// 如果需要约束某个对象必须具备多个接口,则可以是继承的方式创建一个新的接口 然后继承需要约束的接口, 如 [T AB] 这里的AB就继承了2个接口 见后面代码

// 定义对象 用来存放数据
type Person struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

// 定义Ability能力接口
type Ability interface {
	Speech() string //演讲方法
	Swim()          //游泳方法
}

// 为对象Person实现Ability接口
func (p *Person) Speech() string {
	return fmt.Sprintf("%v正在演讲", p.Name)
}
func (p *Person) Swim() {
	fmt.Printf("%v正在游泳\n", p.Name)
}

// Person的其他普通方法
func (p *Person) Exam() {
	fmt.Printf("%v正在考试\n", p.Name)
}

// 演讲测试
func SpeechDemo[T interface {
	Speech() string
	Swim()
}](people T) {
	str := people.Speech() // 因为T约束了这个people必须具备Speech这个方法,所以这里才可以直接调用
	fmt.Println(str)
	people.Swim()
}

// 约束people必须是拥有 Speech()和Exam()方法的对象,
func SpeechDemo2[T interface {
	Speech() string
	Exam()
}](people T) {
	str := people.Speech() // 因为T约束了这个people必须具备Speech这个方法,所以这里才可以直接调用
	fmt.Println(str)
	people.Exam()
}

// 这个当前的Person对象中没有Flight()方法, 调用直接编译错误
func SpeechDemo3[T interface {
	Speech() string
	Flight()
}](people T) {
	str := people.Speech() // 因为T约束了这个people必须具备Speech这个方法,所以这里才可以直接调用
	fmt.Println(str)
	people.Flight()
}

// 泛型参数直接约束接口, 当然这里的接口可以是多个 使用|分隔即可 ,如 [T Aaa| Bbb ]表示T必须是实现了Aaa或者Bbb
func SpeechDemo4[T Ability](people T) {
	str := people.Speech() // 因为T约束了这个people必须具备Speech这个方法,所以这里才可以直接调用
	fmt.Println(str)
	people.Swim()
	// people.Flight() //这行代码 直接编译错误 因为Ability接口中没有这个方法
}

type Bbb interface {
	Flight()
}

// 继承Ability和Bbb接口
type AB interface {
	Ability
	Bbb
}

// 这里使用了接口继承的方式来约束这里的people必须实现了 Ability接口和Bbb接口
func SpeechDemo5[T AB](people T) {
	str := people.Speech() // 因为T约束了这个people必须具备Speech这个方法,所以这里才可以直接调用
	fmt.Println(str)
	people.Swim()
	people.Flight()
}

单元测试用例

Go 复制代码
// person_test.go
package genricargs

import (
	"testing"
)

// 单元测试用例
// TestXxx和 ExampleXxx使用示例
// 这2种方法的区别在于Test需要带上参数 t *testing.T 而ExampleXxx则不需要, 带参数的TestXxx可以调用 t里面的一系列方法
// 他们都可以使用Output来指定期望的输出
// testing官方手册  https://pkg.go.dev/testing@go1.22.3
// 测试运行命令
// go test -run "        # Run all tests.
// go test -run Foo       # Run top-level tests matching "Foo", such as "TestFooBar".
// go test -run Foo/A=    # For top-level tests matching "Foo", run subtests matching "A=".
// go test -run /A=1      # For all top-level tests, run subtests matching "A=1".
// go test -fuzz FuzzFoo  # Fuzz the target matching "FuzzFoo"
//
// 使用 TestXxx语法进行测试
func TestSpeechDemo(t *testing.T) {
	p1 := &Person{Name: "John", Age: 18}
	SpeechDemo(p1)  // 这里需要自己判断测试是否通过 如果未通过使用 t.Fatalf来格式化输出异常信息(或者函数抛异常也会是失败), 否则使用 t.Logf
	t.Fatal("测试失败") //直接指定这个测试是失败的 这时就会输出所有的控制台结果 包括被测试对象中使用fmt.Print打印的结果
	t.Log("abc ok")

	// 点击函数上方的 run test 这个会输出以下信息
	// go test -timeout 30s -run ^TestSpeechDemo$ tekin.cn/golearn/src/interface/genric_args

	// John正在演讲
	// John正在游泳
	// --- FAIL: TestSpeechDemo (0.00s)
	//
	//	/xxx/golang_learn_mod/src/interface/genric_args/person_test.go:29: 测试失败
	//
	// FAIL
	// FAIL	tekin.cn/golearn/src/interface/genric_args	0.281s
	// FAIL
}

// 使用 ExampleXxx方法进行测试
func ExampleSpeechDemo2() {
	p := &Person{Name: "Alex", Age: 20}
	SpeechDemo2(p)
	// output:Alex正在演讲
	// Alex正在考试
}

func ExampleSpeechDemo3(t *testing.T) {
	// p := &Person{Name: "Alex", Age: 20}
	// SpeechDemo3(p) //这里直接报语法错误 无法编译 因为 参数p缺少方法 Flight
	// output: T (type *Person) does not satisfy interface{Flight(); Speech() string} (missing method Flight)
}
相关推荐
microrain17 小时前
读数是 1008,用电量是 8:SagooIoT 计算服务的增量口径与七种计算策略
物联网·golang·计算服务·sagooiot
看浪的路人19 小时前
第7讲:大模型后端服务化——Go 实现高并发 LLM Proxy / Router / Fallback
开发语言·后端·golang
灯澜忆梦21 小时前
【Go Zap 日志库】#1 |快速上手 + 配置详解
开发语言·后端·golang
不是万能的豆学长1 天前
排查Go服务40天后的内存泄漏问题
开发语言·后端·golang·go
tachibana21 天前
Go 语言 Map 的底层实现原理
开发语言·后端·golang·make
tachibana21 天前
Go语言里slice是怎么扩容的?
后端·golang·go·切片
西瓜很甜哟1 天前
golang实现大文件分片上传
后端·golang·html
tachibana22 天前
使用for range 的时候,它的地址会发生变化吗?
开发语言·后端·golang·go·语法
王中阳Go2 天前
Go 后端转 AI:非科班怎么补?一张 3 列路线表(可保存)
开发语言·人工智能·后端·python·golang·大语言模型
for_ever_love__2 天前
LangChain Agentic RAG:让 Agent 自己决定要不要检索、检索几次
人工智能·golang·langchain·rag·langgraph·agentic·自适应检索