从零开始学GO ---- 接口

本文详细介绍了Go语言的接口,包括接口声明、初始化、方法调用、动态类型与静态类型以及接口运算,如接口类型断言和查询。通过示例解释了如何在Go中实现和使用接口,强调接口的非侵入式特性和编译时的类型检查。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从零开始学GO ---- 接口

接口是一个编程规范,一组方法签名的集合。Go的接口是非侵入式的设置,一个具体类型实现接口不需要在语法上显式地声明,只要具体类型的方法集是接口方法集的超集,就代表该类型实现了接口,编译器在编译时进行方法的校验。接口定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节。

在Go语言中接口(interface)是一种类型,一种抽象的类型。

接口声明

Go语言接口分为 接口字面变量类型和接口命名类型,用interface关键字声明

接口字面变量类型的声明语法:

interface{
	MethodSignature1
	MethodSignature2
}

接口命名类型使用type关键字声明:

type InterfaceName interface{
	MethodSignature1
	MethodSignature2
}

接口定义的大括号内是方法声明的集合,也可以嵌入另一个接口类型匿名字段。

例如:

type Reader interface{
    Read(p []byte) (n int,err error)
}
type Writer interface{
    Writer(p []byte)(n int,err error)
}

type ReadWriter interface{
    Reader
    Writer
}
//《====》等价于
type ReadWriter interface{
    Read(p []byte)(n int,err error)
    Writer(p []byte)(n int,err error)
}

声明新接口类型的特定:

  • 接口的命名一般以"er"结尾
  • 接口定义的内部方法不需要func引导
  • 在接口定义中,只有方法声明没有方法实现
接口初始化

一个对象只要全部实现了接口中的方法,那么就实现了这个接口。换句话说,接口就是一个需要实现的方法列表,因此实现了该接口中所有的方法即实现了接口的初始化。

接口初始化的例子:

// Sayer 接口
type Sayer interface {
	say()
}
//dog和cat接口体
type dog struct {}
type cat struct {}
//因为Sayer接口里只有一个say方法,所以只需要给dog和cat 分别实现say方法就可以实现Sayer接口了。
// dog实现了Sayer接口
func (d dog) say() {
	fmt.Println("汪汪汪")
}

// cat实现了Sayer接口
func (c cat) say() {
	fmt.Println("喵喵喵")
}

func main() {
	var x Sayer // 声明一个Sayer类型的变量x
	a := cat{}  // 实例化一个cat
	b := dog{}  // 实例化一个dog
    //由于cat和dog都实现了Sayer接口,因此Sayer接口变量可以被赋值为cat和dog
	x = a       // 将cat实例直接赋值给x
	x.say()     // 喵喵喵
	x = b       // 将dog实例直接赋值给x
	x.say()     // 汪汪汪
}
接口方法调用

接口方法调用的最终地址是在运行期决定的,将具体类型赋值给接口后,会使用具体类型的方法指针初始化接口变量,当调用接口变量的方法时,节间调用实例的方法。接口方法的调用不是一种直接调用,有一定的运行时开销。

直接调用未初始化的接口变量会产生 panic.

package main

type Printer interface {
	Print()
}

type S struct{}

func (s S) Print() {
	println("print")
}
func main() {
	var i Printer
	//未初始化的接口调用方法会产生panic
	//i.Print()
	//

	i = S{}   //进行初始化
	i.Print() //运行时简介调用S.Print
	//print
}
动态类型和静态类型

动态类型: 接口绑定的具体实例的类型称为接口的动态类型,动态类型随着不同的绑定会发生变化

静态类型: 接口被定义时,其类型就完全被确定,这个类型就是接口的静态类型,静态类型本质特征是接口方法签名集合,两个接口如果方法集合相同的话则静态类型一致可以相互赋值。如果a接口的方法集A,b接口的方法集B,其实B是A的子集合,则a的接口变量可以直接赋值给b的接口变量。

接口运算
接口类型断言

接口断言的语法形式:i.(TypeName) i是接口变量,TypeName可以是接口类型名,也可以是具体类型名

接口查询的两层语义:

  • 如果TypeName是一个具体类型名,则类型断言用来判断接口变量i绑定的实例类型是否就是具体类型TypeName
  • 如果TypeName是一个接口类型名,则类型断言可以判断接口变量i绑定的实例类型是否同时实现了TypeName接口

接口断言的两种语法表现:

直接赋值模式

o:=i.(TypeName)

  • 如果Typename是具体类型名,并且接口i绑定的实例类型就是具体类型TypeName,则变量o的类型就是TypeName,变量o的值是接口绑定的实例值的副本
  • 如果Typename是接口类型名,如果接口i绑定的实例类型满足接口类型TypeName,则变量o的类型就是TypeName,变量o底层绑定的具体类型实例就是i绑定的实例的副本
  • 否则,程序抛出panic
package main

import "fmt"

//实现一个内部接口
type Inter interface {
	Ping()
	Pang()
}

//实现一个外部接口
type Anter interface {
	Inter //内部接口
	String()
}

type St struct {
	Name string
}

//为 St实现Inter的接口方法集
func (St) Ping() {
	fmt.Println("Ping")
}

func (*St) Pang() {
	fmt.Println("Pang")
}

func main() {
	st := &St{"Tom"}
	var i interface{} = st //定义一个空接口绑定到st

	o := i.(Inter) //判断i绑定的实例是否实现了接口类型Inter
	o.Ping()       //Ping
	o.Ping()       //Pang

	//p := i.(Anter) //判断i绑定的实例是否实现了接口类型Anter
	//p.String()
	//panic: interface conversion: *main.St is not main.Anter: missing method String
	//因为i没有实现String故没有实现Anter,会引发panic

	s := i.(*St) //判断i绑定的实例是否就是具体类型St
	fmt.Printf("%s", s.Name)   //Tom
}

comma, ok 表达式模式:

if o,ok:=i.(TypeName);ok{ }

  • TypeName是具体类型名,接口i绑定的实例类型就是具体类型TypeName,则oktrue,变量o类型就是TypeName,变量o的值就是接口绑定的实例值的副本
  • TypeName是接口类型名,接口i绑定的实例类型满足接口类型TypeName,则oktrue,变量o类型就是接口类型TypeName,变量o底层绑定的具体类型是i绑定的实例的副本
  • 否则,okfalse,变量oTypeName类型的零值,此种条件下的分支逻辑不应该再去引用o
package main

import "fmt"

//实现一个内部接口
type Inter interface {
	Ping()
	Pang()
}

//实现一个外部接口
type Anter interface {
	Inter //内部接口
	String()
}

type St struct {
	Name string
}

//为 St实现Inter的接口方法集
func (St) Ping() {
	fmt.Println("Ping")
}

func (*St) Pang() {
	fmt.Println("Pang")
}

func main() {
	st := &St{"Tom"}
	var i interface{} = st //定义一个空接口绑定到st

	if o, ok := i.(Inter); ok { //判断i绑定的实例是否实现了接口类型Inter
		o.Ping() //Ping
		o.Pang() //Pang
	}

	if p, ok := i.(Anter); ok {
		//由于i没有实现接口Anter,故ok为false,程序不执行到这里
		p.String()
	}

	if s, ok := i.(*St); ok {
		fmt.Printf("%s", s.Name) //Tom
	}

}

接口类型查询

接口类型查询语法:

switch v:=i.(type){
case type1:
	xxxx
case type2:
    xxxx
default:
    xxxx
}
  • i是一个接口类型
  • case后可以接接口类型名,也可以接 非接口类型名
    • case 接口类型名,如果i绑定的实例类型实现了该接口类型的方法则匹配成功,v的类型是接口类型,v底层绑定的实例是i绑定具体类型实例的副本
    • case 具体类型名,如果接口变量i绑定的实例类型和该具体类型相同,则匹配成功,此时v就是该具体类型变量,v的值是i绑定的实例值的副本
    • 均不匹配的话执行default,v的值为0,没有意义
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值