12-Go语言之接口(interface)

内容目录

  • 接口

内容目录

接口定义

  • GO语言是面向接口编程
  • 接口是一种抽象的类型
  • 接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节。

每个接口由数个方法组成,接口的定义格式如下:

type 接口类型名 interface{
    方法名1( 参数列表1 ) 返回值列表1
    方法名2( 参数列表2 ) 返回值列表2
    …
}

其中:

  • 接口名:使用type将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口叫Writer,有字符串功能的接口叫Stringer等。接口名最好要能突出该接口的类型含义。
  • 方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
  • 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。

实现接口的条件

  • 一个对象只要全部实现了接口中的方法,那么就实现了这个接口。换句话说,接口就是一个需要实现的方法列表。我们来定义一个Sayer接口:
  • 注意:接口内只有方法
// Sayer 接口
type Sayer interface {
	say()
}

定义dogcat两个结构体:

type dog struct {}

type cat struct {}

因为Sayer接口里只有一个say方法,所以我们只需要给dogcat 分别实现say方法就可以实现Sayer接口了。

// dog实现了Sayer接口
func (d dog) say() {
	fmt.Println("汪汪汪")
}

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

接口的实现就是这么简单,只要实现了接口中的所有方法,就实现了这个接口。

接口使用

多态特性

  • 使用接口可以实现面向对象中的多态,任何实现了接口中的方法,都可以用接口调用其本身的方法

    // 接口实现一个人的特性
    type Humaner interface {
    	Say()
    }
    // 定义学生结构体
    type Student struct {
    	name  string
    	score int
    }
    // 定义学生方法
    func (s *Student) Say() {
    	fmt.Printf("Student[%s,%d]瞌睡不断
    ", s.name, s.score)
    }
    // 定义老师结构体
    type Teacher struct {
    	name  string
    	group string
    }
    // 定义老师方法
    func (t *Teacher) Say() {
    	fmt.Printf("Teacher[%s,%s]毁人不倦
    ", t.name, t.group)
    }
    // 自定义类型
    type MyStr string
    // 自定义类型方法
    func (str MyStr) Say() {
    	fmt.Printf("MyStr[%s]原类型是string
    ", str)
    }
    // 定义接口方法
    func WhoSay(i Humaner){
    	i.Say()
    }
    
    func main() {
    	s := &Student{"阿杰",88}		// 实例化学生
    	t := &Teacher{"李老师","GO语言二期"}// 实例化老师
    	var tmp MyStr = "毒鸡汤"		// 实例化自定义类型
    
    	// 方式一:接口调用,多态:
    	WhoSay(s)
    	WhoSay(t)
    	WhoSay(tmp)
    
    	// 方式二:接口调用,利用循环去调用方法
    	x := make([]Humaner,3)
    	x[0] ,x[1],x[2] = s,t,tmp
    	for _,value := range x{
    		value.Say()
    	}
    }
    /* 
    方式一结果:
    Student[阿杰,88]瞌睡不断
    Teacher[李老师,GO语言二期]毁人不倦
    MyStr[毒鸡汤]原类型是string
    
    方式二结果:
    Student[阿杰,88]瞌睡不断
    Teacher[李老师,GO语言二期]毁人不倦
    MyStr[毒鸡汤]原类型是string
    */
    

继承

  • 使用一个接口继承另一个接口,也会继承接口的方法:

    // 接口实现一个人的特性
    type Humaner interface {
    	Say()
    }
    // 用另一个接口继承Humaner接口
    type Personer interface {
    	Humaner
    	Sing(lyrics string)
    }
    // 定义学生结构体
    type Student struct {
    	name  string
    	score int
    }
    // 定义学生方法
    func (s *Student) Say() {
    	fmt.Printf("Student[%s,%d]瞌睡不断
    ", s.name, s.score)
    }
    // 定义学生方法
    func (s *Student) Sing(lyrics string) {
    	fmt.Printf("Student sing[%s]
    ", lyrics)
    }
    
    func main() {
    	s := &Student{"阿杰",88}		// 实例化学生
    	var p Personer		// 定义接口类型的变量
    	p = s				// 赋值操作
    	p.Say()				// 调用继承的方法
    	p.Sing("葫芦娃")	// 调用继承的方法
    }
    /*
    Student[阿杰,88]瞌睡不断
    Student sing[葫芦娃]
    */
    

接口接收变量

  • 接口可以接收任何实现了该接口内方法的变量。

  • 接口类型变量:可以存储任何实现了该接口所有方法的对象类型。

    // 接口实现一个动物的特性
    type Animal interface {
    	Talk()
    	Eat()
    	Name()	string	// 方法的返回值,直接写
    }
    
    // 定义结构体
    type Dog struct {
    }
    
    func (d Dog)Talk(){
    	fmt.Println("旺旺旺")
    }
    func (d Dog)Eat(){
    	fmt.Println("啃骨头")
    }
    func (d Dog)Name()string{
    	fmt.Println("名字,旺财")
    	return "旺财"
    }
    
    func main() {
    	var a Animal	// a相当于是一个变量
    	fmt.Println(a)	// 此时a为空值,nil
    	var d Dog
    	// 接口类型可以存放Dog结构体,因为Dog实现了接口所有方法
    	a = d		// 类绑定变量
    	a.Talk()
    	a.Eat()
    	a.Name()
    }
    /*
    <nil>
    旺旺旺
    啃骨头
    名字,旺财
    */
    

同一类型实现多个接口

  • 同一个类型可以实现多个接口

    // 接口实现一个动物的特性
    type Animal interface {
    	Talk()
    	Eat()
    }
    // 第二个接口
    type Animal2 interface {
    	Run()
    }
    
    // 定义结构体
    type Dog struct {
    }
    // 定义方法
    func (d *Dog) Run() {
    	fmt.Println("奔跑吧旺财")
    }
    func (d *Dog) Talk() {
    	fmt.Println("旺旺旺")
    }
    func (d *Dog) Eat() {
    	fmt.Println("啃骨头")
    }
    
    func main() {
    	var a Animal
    	var d Dog
    	a = &d
    	a.Eat()
    
    	var a2 Animal2
    	a2 = &d
    	a.Talk()	// 同一个结构体可以实现多个接口
    	a2.Run()	// 实现第二个接口
    }
    /*
    啃骨头
    奔跑吧旺财
    */
    

接口嵌套

  • 接口嵌套后,必须实现嵌套接口内的所有方法才可使用

    // 接口实现一个动物的特性
    type Animal interface {
    	Talk()
    	Eat()
    }
    // 第二个接口
    type Animal2 interface {
    	Run()
    }
    // 第三个接口
    type Animal3 interface {
    	Animal		// 嵌套第一个接口
    	Animal2		// 嵌套第二个接口
    }
    

空接口

  • 空接口是指没有定义任何方法的接口,因此任何类型都实现了空接口。

  • 空接口类型的变量可以存储任意类型的变量。

    func main(){
    	var x interface{}	// 初始化一个空接口
    	x = 100				// 赋值操作
    	fmt.Println(x)		// 打印的是int类型的100
    	x = "沙河"
    	fmt.Println(x)		// 输出为string类型
    	x = false
    	fmt.Println(x)		// 输出bool类型
    	x = struct {
    		name string
    	}{name:"花花"}
    	fmt.Println(x)		// 输出结构体类型
    }
    

空接口的应用

空接口作为函数的参数
  • 使用空接口实现可以接收任意类型的函数参数

    // 空接口作为函数参数
    func showType(a interface{}){
    	fmt.Printf("type:%T
    ",a)
    }
    
    func main(){
    	showType(100)
    	showType(99.99)
    	showType(time.Second)
    }
    // type:int
    // type:float64
    // type:time.Duration
    
空接口作为map的值
  • 使用空接口实现可以保存任意值的字典。

    func main(){
    	// 定义一个值为空接口的map
    	var stuInfo = make(map[string]interface{},100)
    	stuInfo["阿杰"]	= 100
    	stuInfo["阿豪"]	= true
    	stuInfo["阿呆"]	= 99.99
    	stuInfo["阿水"]	= "阿珍爱上了阿强"
    	stuInfo["时间"]	= time.Now()
    	fmt.Println(stuInfo)
    }
    /* 
    map[
    时间:2020-10-19 17:45:51.4885587 +0800 CST m=+0.004011601 
    阿呆:99.99 
    阿杰:100 
    阿水:阿珍爱上了阿强 
    阿豪:true]
    */
    

值接收者和指针接收者实现接口的区别

  • 值类型实现接口,指针类型可以存进去

  • 但指针类型实现接口,值类型存不进去

    • 示例一:值类型实现接口,指针类型可以存进去
    // 接口实现一个动物的特性
    type Animal interface {
    	Talk()
    	Eat()
    	Name()	string	// 方法的返回值,直接写
    }
    
    // 定义结构体
    type Dog struct {
    }
    
    func (d Dog)Talk(){
    	fmt.Println("旺旺旺")
    }
    func (d Dog)Eat(){
    	fmt.Println("啃骨头")
    }
    func (d Dog)Name()string{
    	fmt.Println("名字,旺财")
    	return "旺财"
    }
    func main() {
    	var a Animal	// a相当于是一个变量
    	var d *Dog = &Dog{}	// 定义Dog为指针类型
    	a = d		// 类绑定变量
    	// 调用时,内部自动转*(&Dag).Eat()
    	a.Talk()
    	a.Eat()
    	a.Name()
    }
    
    • 示例二:指针类型实现接口,值类型存不进去
    // 接口实现一个动物的特性
    type Animal interface {
    	Talk()
    	Eat()
    	Name()	string	// 方法的返回值,直接写
    }
    
    // 定义结构体
    type Dog struct {
    }
    
    func (d *Dog)Talk(){
    	fmt.Println("旺旺旺")
    }
    func (d *Dog)Eat(){
    	fmt.Println("啃骨头")
    }
    func (d *Dog)Name()string{
    	fmt.Println("名字,旺财")
    	return "旺财"
    }
    
    func main() {
    	var a Animal	// a相当于是一个变量
    	var d Dog
    	// 以下代码编译不通过原因:
    	// 若传值类型,不能获取变量地址,d取不到地址,寻址问题不通过
    	a = d		// 类绑定变量
    	a.Talk()
    	a.Eat()
    	a.Name()
    }
    

类型断言

  • 想要判断空接口中的值这个时候就可以使用类型断言,语法格式为:

  • 该语法返回两个参数,第一个参数是x转化为T类型后的变量,第二个值是一个布尔值,若为true则表示断言成功,为false则表示断言失败。

    x.(T)
    

    其中:

    • x:表示类型为interface{}的变量
    • T:表示断言x可能是的类型。
  • 例一:

    // 示例一:
    func main() {
    	var x interface{}
    	x = "Hello 北京"
    	v, ok := x.(string)			// 进行类型断言
    	if ok {
    		fmt.Println(v)
    	} else {
    		fmt.Println("类型断言失败")
    	}
    }
    
    // 示例二:comma-ok断言
    type Element interface {
    }
    type Person struct {
    	name string
    	age  int
    }
    
    func main() {
    	list := make([]Element, 3)
    	list[0] = 1
    	list[1] = "hello"
    	list[2] = Person{"小强", 18}
    	for index, element := range list {
    		// 类型断言:value,ok := 元素.(Type)
    		if value, ok := element.(int); ok {
    			fmt.Printf("list[%d]是int类型,值是%d
    ", index, value)
    		} else if value, ok := element.(string); ok {
    			fmt.Printf("list[%d]是string类型,值是%v
    ", index, value)
    		} else if value, ok := element.(Person); ok {
    			fmt.Printf("list[%d]是Person类型,值是[%s,%d]
    ", index, value.name, value.age)
    		} else {
    			fmt.Println("不支持的类型")
    		}
    	}
    }
    
  • 例二:可以用switch语句实现多种类型断言

    func justifyType(x interface{}) {
    	switch v := x.(type) {
    	case string:
    		fmt.Printf("x is a string,value is %v
    ", v)
    	case int:
    		fmt.Printf("x is a int,value  is %v
    ", v)
    	case bool:
    		fmt.Printf("x is a bool,value  is %v
    ", v)
    	case *string:
    		fmt.Printf("x is a string poninter,value  is %v
    ", v)
    	default:
    		fmt.Println("unsupport type!")
    	}
    }
    
    func main(){
    	justifyType(100)
    	s := "你好"
    	justifyType(&s)
    }
    /*
    x is a int,value  is 100
    x is a string poninter,value  is 0xc00002e1f0
    */
    
原文地址:https://www.cnblogs.com/lynlearnde/p/13841950.html